aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/videos/+video-watch/comment
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-02-11 11:52:34 +0100
committerChocobozzz <me@florianbigard.com>2019-02-11 11:52:34 +0100
commit88108880bbdba473cfe36ecbebc1c3c4f972e102 (patch)
treeb242efb3b4f0d7e49d88f2d1f2063b5b3b0489c0 /client/src/app/videos/+video-watch/comment
parent53a94c7cfa8368da4cd248d65df8346905938f0c (diff)
parent9b712a2017e4ab3cf12cd6bd58278905520159d0 (diff)
downloadPeerTube-88108880bbdba473cfe36ecbebc1c3c4f972e102.tar.gz
PeerTube-88108880bbdba473cfe36ecbebc1c3c4f972e102.tar.zst
PeerTube-88108880bbdba473cfe36ecbebc1c3c4f972e102.zip
Merge branch 'develop' into pr/1217
Diffstat (limited to 'client/src/app/videos/+video-watch/comment')
-rw-r--r--client/src/app/videos/+video-watch/comment/linkifier.service.ts114
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment-add.component.html2
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment-add.component.ts15
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment.component.scss6
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment.component.ts29
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment.model.ts2
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment.service.ts14
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comments.component.html2
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comments.component.scss2
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comments.component.ts37
10 files changed, 36 insertions, 187 deletions
diff --git a/client/src/app/videos/+video-watch/comment/linkifier.service.ts b/client/src/app/videos/+video-watch/comment/linkifier.service.ts
deleted file mode 100644
index 3f4072efd..000000000
--- a/client/src/app/videos/+video-watch/comment/linkifier.service.ts
+++ /dev/null
@@ -1,114 +0,0 @@
1import { Injectable } from '@angular/core'
2import { getAbsoluteAPIUrl } from '@app/shared/misc/utils'
3import * as linkify from 'linkifyjs'
4import * as linkifyHtml from 'linkifyjs/html'
5
6@Injectable()
7export class LinkifierService {
8
9 static CLASSNAME = 'linkified'
10
11 private linkifyOptions = {
12 className: {
13 mention: LinkifierService.CLASSNAME + '-mention',
14 url: LinkifierService.CLASSNAME + '-url'
15 }
16 }
17
18 constructor () {
19 // Apply plugin
20 this.mentionWithDomainPlugin(linkify)
21 }
22
23 linkify (text: string) {
24 return linkifyHtml(text, this.linkifyOptions)
25 }
26
27 private mentionWithDomainPlugin (linkify: any) {
28 const TT = linkify.scanner.TOKENS // Text tokens
29 const { TOKENS: MT, State } = linkify.parser // Multi tokens, state
30 const MultiToken = MT.Base
31 const S_START = linkify.parser.start
32
33 const TT_AT = TT.AT
34 const TT_DOMAIN = TT.DOMAIN
35 const TT_LOCALHOST = TT.LOCALHOST
36 const TT_NUM = TT.NUM
37 const TT_COLON = TT.COLON
38 const TT_SLASH = TT.SLASH
39 const TT_TLD = TT.TLD
40 const TT_UNDERSCORE = TT.UNDERSCORE
41 const TT_DOT = TT.DOT
42
43 function MENTION (value) {
44 this.v = value
45 }
46
47 linkify.inherits(MultiToken, MENTION, {
48 type: 'mentionWithDomain',
49 isLink: true,
50 toHref () {
51 return getAbsoluteAPIUrl() + '/services/redirect/accounts/' + this.toString().substr(1)
52 }
53 })
54
55 const S_AT = S_START.jump(TT_AT) // @
56 const S_AT_SYMS = new State()
57 const S_MENTION = new State(MENTION)
58 const S_MENTION_DIVIDER = new State()
59 const S_MENTION_DIVIDER_SYMS = new State()
60
61 // @_,
62 S_AT.on(TT_UNDERSCORE, S_AT_SYMS)
63
64 // @_*
65 S_AT_SYMS
66 .on(TT_UNDERSCORE, S_AT_SYMS)
67 .on(TT_DOT, S_AT_SYMS)
68
69 // Valid mention (not made up entirely of symbols)
70 S_AT
71 .on(TT_DOMAIN, S_MENTION)
72 .on(TT_LOCALHOST, S_MENTION)
73 .on(TT_TLD, S_MENTION)
74 .on(TT_NUM, S_MENTION)
75
76 S_AT_SYMS
77 .on(TT_DOMAIN, S_MENTION)
78 .on(TT_LOCALHOST, S_MENTION)
79 .on(TT_TLD, S_MENTION)
80 .on(TT_NUM, S_MENTION)
81
82 // More valid mentions
83 S_MENTION
84 .on(TT_DOMAIN, S_MENTION)
85 .on(TT_LOCALHOST, S_MENTION)
86 .on(TT_TLD, S_MENTION)
87 .on(TT_COLON, S_MENTION)
88 .on(TT_NUM, S_MENTION)
89 .on(TT_UNDERSCORE, S_MENTION)
90
91 // Mention with a divider
92 S_MENTION
93 .on(TT_AT, S_MENTION_DIVIDER)
94 .on(TT_SLASH, S_MENTION_DIVIDER)
95 .on(TT_DOT, S_MENTION_DIVIDER)
96
97 // Mention _ trailing stash plus syms
98 S_MENTION_DIVIDER.on(TT_UNDERSCORE, S_MENTION_DIVIDER_SYMS)
99 S_MENTION_DIVIDER_SYMS.on(TT_UNDERSCORE, S_MENTION_DIVIDER_SYMS)
100
101 // Once we get a word token, mentions can start up again
102 S_MENTION_DIVIDER
103 .on(TT_DOMAIN, S_MENTION)
104 .on(TT_LOCALHOST, S_MENTION)
105 .on(TT_TLD, S_MENTION)
106 .on(TT_NUM, S_MENTION)
107
108 S_MENTION_DIVIDER_SYMS
109 .on(TT_DOMAIN, S_MENTION)
110 .on(TT_LOCALHOST, S_MENTION)
111 .on(TT_TLD, S_MENTION)
112 .on(TT_NUM, S_MENTION)
113 }
114}
diff --git a/client/src/app/videos/+video-watch/comment/video-comment-add.component.html b/client/src/app/videos/+video-watch/comment/video-comment-add.component.html
index b58a56596..d8a7a78c4 100644
--- a/client/src/app/videos/+video-watch/comment/video-comment-add.component.html
+++ b/client/src/app/videos/+video-watch/comment/video-comment-add.component.html
@@ -3,7 +3,7 @@
3 <img [src]="getAvatarUrl()" alt="Avatar" /> 3 <img [src]="getAvatarUrl()" alt="Avatar" />
4 4
5 <div class="form-group"> 5 <div class="form-group">
6 <textarea i18n-placeholder placeholder="Add comment..." autosize 6 <textarea i18n-placeholder placeholder="Add comment..." myAutoResize
7 [readonly]="(user === null) ? true : false" 7 [readonly]="(user === null) ? true : false"
8 (click)="openVisitorModal($event)" 8 (click)="openVisitorModal($event)"
9 formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }" 9 formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }"
diff --git a/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts b/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts
index fb7de0e04..fd85c28f2 100644
--- a/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts
+++ b/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts
@@ -1,6 +1,6 @@
1import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' 1import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
2import { Router } from '@angular/router' 2import { Router } from '@angular/router'
3import { NotificationsService } from 'angular2-notifications' 3import { Notifier } from '@app/core'
4import { Observable } from 'rxjs' 4import { Observable } from 'rxjs'
5import { VideoCommentCreate } from '../../../../../../shared/models/videos/video-comment.model' 5import { VideoCommentCreate } from '../../../../../../shared/models/videos/video-comment.model'
6import { FormReactive } from '../../../shared' 6import { FormReactive } from '../../../shared'
@@ -29,14 +29,14 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
29 @Output() commentCreated = new EventEmitter<VideoCommentCreate>() 29 @Output() commentCreated = new EventEmitter<VideoCommentCreate>()
30 30
31 @ViewChild('visitorModal') visitorModal: NgbModal 31 @ViewChild('visitorModal') visitorModal: NgbModal
32 @ViewChild('textarea') private textareaElement: ElementRef 32 @ViewChild('textarea') textareaElement: ElementRef
33 33
34 private addingComment = false 34 addingComment = false
35 35
36 constructor ( 36 constructor (
37 protected formValidatorService: FormValidatorService, 37 protected formValidatorService: FormValidatorService,
38 private videoCommentValidatorsService: VideoCommentValidatorsService, 38 private videoCommentValidatorsService: VideoCommentValidatorsService,
39 private notificationsService: NotificationsService, 39 private notifier: Notifier,
40 private videoCommentService: VideoCommentService, 40 private videoCommentService: VideoCommentService,
41 private authService: AuthService, 41 private authService: AuthService,
42 private modalService: NgbModal, 42 private modalService: NgbModal,
@@ -70,13 +70,13 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
70 } 70 }
71 71
72 onValidKey () { 72 onValidKey () {
73 this.onValueChanged() 73 this.check()
74 if (!this.form.valid) return 74 if (!this.form.valid) return
75 75
76 this.formValidated() 76 this.formValidated()
77 } 77 }
78 78
79 openVisitorModal (event) { 79 openVisitorModal (event: any) {
80 if (this.user === null) { // we only open it for visitors 80 if (this.user === null) { // we only open it for visitors
81 // fixing ng-bootstrap ModalService and the "Expression Changed After It Has Been Checked" Error 81 // fixing ng-bootstrap ModalService and the "Expression Changed After It Has Been Checked" Error
82 event.srcElement.blur() 82 event.srcElement.blur()
@@ -115,7 +115,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
115 err => { 115 err => {
116 this.addingComment = false 116 this.addingComment = false
117 117
118 this.notificationsService.error(this.i18n('Error'), err.text) 118 this.notifier.error(err.text)
119 } 119 }
120 ) 120 )
121 } 121 }
@@ -135,7 +135,6 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
135 135
136 gotoLogin () { 136 gotoLogin () {
137 this.hideVisitorModal() 137 this.hideVisitorModal()
138 this.authService.redirectUrl = this.router.url
139 this.router.navigate([ '/login' ]) 138 this.router.navigate([ '/login' ])
140 } 139 }
141 140
diff --git a/client/src/app/videos/+video-watch/comment/video-comment.component.scss b/client/src/app/videos/+video-watch/comment/video-comment.component.scss
index 84da5727e..731ecbf8f 100644
--- a/client/src/app/videos/+video-watch/comment/video-comment.component.scss
+++ b/client/src/app/videos/+video-watch/comment/video-comment.component.scss
@@ -41,7 +41,7 @@
41 } 41 }
42 42
43 .comment-date { 43 .comment-date {
44 color: #585858; 44 color: $grey-foreground-color;
45 margin-left: 10px; 45 margin-left: 10px;
46 } 46 }
47 } 47 }
@@ -69,7 +69,7 @@
69 69
70 .comment-action-reply, 70 .comment-action-reply,
71 .comment-action-delete { 71 .comment-action-delete {
72 color: #585858; 72 color: $grey-foreground-color;
73 cursor: pointer; 73 cursor: pointer;
74 margin-right: 10px; 74 margin-right: 10px;
75 75
@@ -108,4 +108,4 @@
108 .root-comment { 108 .root-comment {
109 font-size: 14px; 109 font-size: 14px;
110 } 110 }
111} \ No newline at end of file 111}
diff --git a/client/src/app/videos/+video-watch/comment/video-comment.component.ts b/client/src/app/videos/+video-watch/comment/video-comment.component.ts
index e90008de9..aba7f9d1c 100644
--- a/client/src/app/videos/+video-watch/comment/video-comment.component.ts
+++ b/client/src/app/videos/+video-watch/comment/video-comment.component.ts
@@ -1,11 +1,10 @@
1import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core' 1import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'
2import { LinkifierService } from '@app/videos/+video-watch/comment/linkifier.service'
3import * as sanitizeHtml from 'sanitize-html'
4import { UserRight } from '../../../../../../shared/models/users' 2import { UserRight } from '../../../../../../shared/models/users'
5import { VideoCommentThreadTree } from '../../../../../../shared/models/videos/video-comment.model' 3import { VideoCommentThreadTree } from '../../../../../../shared/models/videos/video-comment.model'
6import { AuthService } from '../../../core/auth' 4import { AuthService } from '../../../core/auth'
7import { Video } from '../../../shared/video/video.model' 5import { Video } from '../../../shared/video/video.model'
8import { VideoComment } from './video-comment.model' 6import { VideoComment } from './video-comment.model'
7import { HtmlRendererService } from '@app/shared/renderer'
9 8
10@Component({ 9@Component({
11 selector: 'my-video-comment', 10 selector: 'my-video-comment',
@@ -26,10 +25,10 @@ export class VideoCommentComponent implements OnInit, OnChanges {
26 @Output() resetReply = new EventEmitter() 25 @Output() resetReply = new EventEmitter()
27 26
28 sanitizedCommentHTML = '' 27 sanitizedCommentHTML = ''
29 newParentComments = [] 28 newParentComments: VideoComment[] = []
30 29
31 constructor ( 30 constructor (
32 private linkifierService: LinkifierService, 31 private htmlRenderer: HtmlRendererService,
33 private authService: AuthService 32 private authService: AuthService
34 ) {} 33 ) {}
35 34
@@ -87,27 +86,7 @@ export class VideoCommentComponent implements OnInit, OnChanges {
87 } 86 }
88 87
89 private init () { 88 private init () {
90 // Convert possible markdown to html 89 this.sanitizedCommentHTML = this.htmlRenderer.toSafeHtml(this.comment.text)
91 const html = this.linkifierService.linkify(this.comment.text)
92
93 this.sanitizedCommentHTML = sanitizeHtml(html, {
94 allowedTags: [ 'a', 'p', 'span', 'br' ],
95 allowedSchemes: [ 'http', 'https' ],
96 allowedAttributes: {
97 'a': [ 'href', 'class', 'target' ]
98 },
99 transformTags: {
100 a: (tagName, attribs) => {
101 return {
102 tagName,
103 attribs: Object.assign(attribs, {
104 target: '_blank',
105 rel: 'noopener noreferrer'
106 })
107 }
108 }
109 }
110 })
111 90
112 this.newParentComments = this.parentComments.concat([ this.comment ]) 91 this.newParentComments = this.parentComments.concat([ this.comment ])
113 } 92 }
diff --git a/client/src/app/videos/+video-watch/comment/video-comment.model.ts b/client/src/app/videos/+video-watch/comment/video-comment.model.ts
index fe591811e..824fb24c3 100644
--- a/client/src/app/videos/+video-watch/comment/video-comment.model.ts
+++ b/client/src/app/videos/+video-watch/comment/video-comment.model.ts
@@ -14,7 +14,7 @@ export class VideoComment implements VideoCommentServerModel {
14 account: AccountInterface 14 account: AccountInterface
15 totalReplies: number 15 totalReplies: number
16 by: string 16 by: string
17 accountAvatarUrl 17 accountAvatarUrl: string
18 18
19 constructor (hash: VideoCommentServerModel) { 19 constructor (hash: VideoCommentServerModel) {
20 this.id = hash.id 20 this.id = hash.id
diff --git a/client/src/app/videos/+video-watch/comment/video-comment.service.ts b/client/src/app/videos/+video-watch/comment/video-comment.service.ts
index 9bcb4b7de..b8e5878c5 100644
--- a/client/src/app/videos/+video-watch/comment/video-comment.service.ts
+++ b/client/src/app/videos/+video-watch/comment/video-comment.service.ts
@@ -1,7 +1,7 @@
1import { catchError, map } from 'rxjs/operators' 1import { catchError, map } from 'rxjs/operators'
2import { HttpClient, HttpParams } from '@angular/common/http' 2import { HttpClient, HttpParams } from '@angular/common/http'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { lineFeedToHtml } from '@app/shared/misc/utils' 4import { objectLineFeedToHtml } from '@app/shared/misc/utils'
5import { Observable } from 'rxjs' 5import { Observable } from 'rxjs'
6import { ResultList, FeedFormat } from '../../../../../../shared/models' 6import { ResultList, FeedFormat } from '../../../../../../shared/models'
7import { 7import {
@@ -28,22 +28,22 @@ export class VideoCommentService {
28 28
29 addCommentThread (videoId: number | string, comment: VideoCommentCreate) { 29 addCommentThread (videoId: number | string, comment: VideoCommentCreate) {
30 const url = VideoCommentService.BASE_VIDEO_URL + videoId + '/comment-threads' 30 const url = VideoCommentService.BASE_VIDEO_URL + videoId + '/comment-threads'
31 const normalizedComment = lineFeedToHtml(comment, 'text') 31 const normalizedComment = objectLineFeedToHtml(comment, 'text')
32 32
33 return this.authHttp.post(url, normalizedComment) 33 return this.authHttp.post<{ comment: VideoCommentServerModel }>(url, normalizedComment)
34 .pipe( 34 .pipe(
35 map(data => this.extractVideoComment(data['comment'])), 35 map(data => this.extractVideoComment(data.comment)),
36 catchError(err => this.restExtractor.handleError(err)) 36 catchError(err => this.restExtractor.handleError(err))
37 ) 37 )
38 } 38 }
39 39
40 addCommentReply (videoId: number | string, inReplyToCommentId: number, comment: VideoCommentCreate) { 40 addCommentReply (videoId: number | string, inReplyToCommentId: number, comment: VideoCommentCreate) {
41 const url = VideoCommentService.BASE_VIDEO_URL + videoId + '/comments/' + inReplyToCommentId 41 const url = VideoCommentService.BASE_VIDEO_URL + videoId + '/comments/' + inReplyToCommentId
42 const normalizedComment = lineFeedToHtml(comment, 'text') 42 const normalizedComment = objectLineFeedToHtml(comment, 'text')
43 43
44 return this.authHttp.post(url, normalizedComment) 44 return this.authHttp.post<{ comment: VideoCommentServerModel }>(url, normalizedComment)
45 .pipe( 45 .pipe(
46 map(data => this.extractVideoComment(data[ 'comment' ])), 46 map(data => this.extractVideoComment(data.comment)),
47 catchError(err => this.restExtractor.handleError(err)) 47 catchError(err => this.restExtractor.handleError(err))
48 ) 48 )
49 } 49 }
diff --git a/client/src/app/videos/+video-watch/comment/video-comments.component.html b/client/src/app/videos/+video-watch/comment/video-comments.component.html
index 42e129d65..44016d8ad 100644
--- a/client/src/app/videos/+video-watch/comment/video-comments.component.html
+++ b/client/src/app/videos/+video-watch/comment/video-comments.component.html
@@ -4,7 +4,7 @@
4 Comments 4 Comments
5 </div> 5 </div>
6 6
7 <my-video-feed [syndicationItems]="syndicationItems"></my-video-feed> 7 <my-feed [syndicationItems]="syndicationItems"></my-feed>
8 </div> 8 </div>
9 9
10 <ng-template [ngIf]="video.commentsEnabled === true"> 10 <ng-template [ngIf]="video.commentsEnabled === true">
diff --git a/client/src/app/videos/+video-watch/comment/video-comments.component.scss b/client/src/app/videos/+video-watch/comment/video-comments.component.scss
index dbb44c66c..575e331e4 100644
--- a/client/src/app/videos/+video-watch/comment/video-comments.component.scss
+++ b/client/src/app/videos/+video-watch/comment/video-comments.component.scss
@@ -23,7 +23,7 @@
23 margin-right: 0; 23 margin-right: 0;
24} 24}
25 25
26my-video-feed { 26my-feed {
27 display: inline-block; 27 display: inline-block;
28 margin-left: 5px; 28 margin-left: 5px;
29} 29}
diff --git a/client/src/app/videos/+video-watch/comment/video-comments.component.ts b/client/src/app/videos/+video-watch/comment/video-comments.component.ts
index c864d82b7..2616820d2 100644
--- a/client/src/app/videos/+video-watch/comment/video-comments.component.ts
+++ b/client/src/app/videos/+video-watch/comment/video-comments.component.ts
@@ -1,17 +1,17 @@
1import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild, ElementRef } from '@angular/core' 1import { Component, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild } from '@angular/core'
2import { ActivatedRoute } from '@angular/router' 2import { ActivatedRoute } from '@angular/router'
3import { ConfirmService } from '@app/core' 3import { ConfirmService, Notifier } from '@app/core'
4import { NotificationsService } from 'angular2-notifications'
5import { Subscription } from 'rxjs' 4import { Subscription } from 'rxjs'
6import { VideoCommentThreadTree } from '../../../../../../shared/models/videos/video-comment.model' 5import { VideoCommentThreadTree } from '../../../../../../shared/models/videos/video-comment.model'
7import { AuthService } from '../../../core/auth' 6import { AuthService } from '../../../core/auth'
8import { ComponentPagination } from '../../../shared/rest/component-pagination.model' 7import { ComponentPagination, hasMoreItems } from '../../../shared/rest/component-pagination.model'
9import { User } from '../../../shared/users' 8import { User } from '../../../shared/users'
10import { VideoSortField } from '../../../shared/video/sort-field.type' 9import { VideoSortField } from '../../../shared/video/sort-field.type'
11import { VideoDetails } from '../../../shared/video/video-details.model' 10import { VideoDetails } from '../../../shared/video/video-details.model'
12import { VideoComment } from './video-comment.model' 11import { VideoComment } from './video-comment.model'
13import { VideoCommentService } from './video-comment.service' 12import { VideoCommentService } from './video-comment.service'
14import { I18n } from '@ngx-translate/i18n-polyfill' 13import { I18n } from '@ngx-translate/i18n-polyfill'
14import { Syndication } from '@app/shared/video/syndication.model'
15 15
16@Component({ 16@Component({
17 selector: 'my-video-comments', 17 selector: 'my-video-comments',
@@ -35,13 +35,13 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
35 threadComments: { [ id: number ]: VideoCommentThreadTree } = {} 35 threadComments: { [ id: number ]: VideoCommentThreadTree } = {}
36 threadLoading: { [ id: number ]: boolean } = {} 36 threadLoading: { [ id: number ]: boolean } = {}
37 37
38 syndicationItems = [] 38 syndicationItems: Syndication[] = []
39 39
40 private sub: Subscription 40 private sub: Subscription
41 41
42 constructor ( 42 constructor (
43 private authService: AuthService, 43 private authService: AuthService,
44 private notificationsService: NotificationsService, 44 private notifier: Notifier,
45 private confirmService: ConfirmService, 45 private confirmService: ConfirmService,
46 private videoCommentService: VideoCommentService, 46 private videoCommentService: VideoCommentService,
47 private activatedRoute: ActivatedRoute, 47 private activatedRoute: ActivatedRoute,
@@ -83,15 +83,11 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
83 this.highlightedThread = new VideoComment(res.comment) 83 this.highlightedThread = new VideoComment(res.comment)
84 84
85 // Scroll to the highlighted thread 85 // Scroll to the highlighted thread
86 setTimeout(() => { 86 setTimeout(() => this.commentHighlightBlock.nativeElement.scrollIntoView(), 0)
87 // -60 because of the fixed header
88 const scrollY = this.commentHighlightBlock.nativeElement.offsetTop - 60
89 window.scroll(0, scrollY)
90 }, 500)
91 } 87 }
92 }, 88 },
93 89
94 err => this.notificationsService.error(this.i18n('Error'), err.message) 90 err => this.notifier.error(err.message)
95 ) 91 )
96 } 92 }
97 93
@@ -103,7 +99,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
103 this.componentPagination.totalItems = res.totalComments 99 this.componentPagination.totalItems = res.totalComments
104 }, 100 },
105 101
106 err => this.notificationsService.error(this.i18n('Error'), err.message) 102 err => this.notifier.error(err.message)
107 ) 103 )
108 } 104 }
109 105
@@ -154,7 +150,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
154 if (this.highlightedThread.id === commentToDelete.id) this.highlightedThread = undefined 150 if (this.highlightedThread.id === commentToDelete.id) this.highlightedThread = undefined
155 }, 151 },
156 152
157 err => this.notificationsService.error(this.i18n('Error'), err.message) 153 err => this.notifier.error(err.message)
158 ) 154 )
159 } 155 }
160 156
@@ -165,22 +161,11 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
165 onNearOfBottom () { 161 onNearOfBottom () {
166 this.componentPagination.currentPage++ 162 this.componentPagination.currentPage++
167 163
168 if (this.hasMoreComments()) { 164 if (hasMoreItems(this.componentPagination)) {
169 this.loadMoreComments() 165 this.loadMoreComments()
170 } 166 }
171 } 167 }
172 168
173 private hasMoreComments () {
174 // No results
175 if (this.componentPagination.totalItems === 0) return false
176
177 // Not loaded yet
178 if (!this.componentPagination.totalItems) return true
179
180 const maxPage = this.componentPagination.totalItems / this.componentPagination.itemsPerPage
181 return maxPage > this.componentPagination.currentPage
182 }
183
184 private deleteLocalCommentThread (parentComment: VideoCommentThreadTree, commentToDelete: VideoComment) { 169 private deleteLocalCommentThread (parentComment: VideoCommentThreadTree, commentToDelete: VideoComment) {
185 for (const commentChild of parentComment.children) { 170 for (const commentChild of parentComment.children) {
186 if (commentChild.comment.id === commentToDelete.id) { 171 if (commentChild.comment.id === commentToDelete.id) {