aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-02-19 10:38:24 +0100
committerChocobozzz <me@florianbigard.com>2018-02-19 10:48:44 +0100
commit1263fc4e6eff9ba4bf4c706c6e37c2e556bf8eb5 (patch)
tree00c27b7e6a81dfc04a8cc86ba782df1e9d0c1db8
parent3bb6c52645af84832212c99fdec04143e4230180 (diff)
downloadPeerTube-1263fc4e6eff9ba4bf4c706c6e37c2e556bf8eb5.tar.gz
PeerTube-1263fc4e6eff9ba4bf4c706c6e37c2e556bf8eb5.tar.zst
PeerTube-1263fc4e6eff9ba4bf4c706c6e37c2e556bf8eb5.zip
Improve comment highlighting
-rw-r--r--CHANGELOG.md4
-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.ts7
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment.component.html5
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment.component.scss18
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment.component.ts21
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment.model.ts1
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comments.component.html17
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comments.component.scss4
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comments.component.ts76
-rw-r--r--client/src/app/videos/+video-watch/video-watch-routing.module.ts5
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts5
12 files changed, 107 insertions, 58 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eadbb1cce..ce7541109 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,10 @@
3 3
4## v0.0.25-alpha 4## v0.0.25-alpha
5 5
6### Features
7
8 * Add ability to link a specific comment
9
6### Bug fixes 10### Bug fixes
7 11
8 * Fix avatars on video watch page 12 * Fix avatars on video watch page
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 865908ba4..e393daa79 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
@@ -4,7 +4,7 @@
4 4
5 <div class="form-group"> 5 <div class="form-group">
6 <textarea placeholder="Add comment..." formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }" 6 <textarea placeholder="Add comment..." formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }"
7 (keyup.control.enter)="formValidated()" (keyup.meta.enter)="formValidated()" #textarea> 7 (keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()" #textarea>
8 8
9 </textarea> 9 </textarea>
10 <div *ngIf="formErrors.text" class="form-error"> 10 <div *ngIf="formErrors.text" class="form-error">
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 3e064efcb..9e499d829 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
@@ -69,6 +69,13 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
69 } 69 }
70 } 70 }
71 71
72 onValidKey () {
73 this.onValueChanged()
74 if (!this.form.valid) return
75
76 this.formValidated()
77 }
78
72 formValidated () { 79 formValidated () {
73 const commentCreate: VideoCommentCreate = this.form.value 80 const commentCreate: VideoCommentCreate = this.form.value
74 let obs: Observable<any> 81 let obs: Observable<any>
diff --git a/client/src/app/videos/+video-watch/comment/video-comment.component.html b/client/src/app/videos/+video-watch/comment/video-comment.component.html
index ec9a236d3..e3049e15b 100644
--- a/client/src/app/videos/+video-watch/comment/video-comment.component.html
+++ b/client/src/app/videos/+video-watch/comment/video-comment.component.html
@@ -2,10 +2,11 @@
2 <img [src]="getAvatarUrl(comment.account)" alt="Avatar" /> 2 <img [src]="getAvatarUrl(comment.account)" alt="Avatar" />
3 3
4 <div class="comment"> 4 <div class="comment">
5 <span class="marked-comment" *ngIf="comment.marked">Marked comment</span> 5 <div *ngIf="highlightedComment === true" class="highlighted-comment">Highlighted comment</div>
6
6 <div class="comment-account-date"> 7 <div class="comment-account-date">
7 <a target="_blank" [href]="comment.account.url" class="comment-account">{{ comment.by }}</a> 8 <a target="_blank" [href]="comment.account.url" class="comment-account">{{ comment.by }}</a>
8 <a [routerLink]="['/videos/watch', video.uuid, 'comment', comment.id]" class="comment-date">{{ comment.createdAt | myFromNow }}</a> 9 <a [routerLink]="['/videos/watch', video.uuid, { 'commentId': comment.id }]" class="comment-date">{{ comment.createdAt | myFromNow }}</a>
9 </div> 10 </div>
10 <div class="comment-html" [innerHTML]="sanitizedCommentHTML"></div> 11 <div class="comment-html" [innerHTML]="sanitizedCommentHTML"></div>
11 12
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 b03bc73d0..3e8892e84 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
@@ -15,6 +15,17 @@
15 .comment { 15 .comment {
16 flex-grow: 1; 16 flex-grow: 1;
17 17
18 .highlighted-comment {
19 display: inline-block;
20 background-color: #F5F5F5;
21 color: #3d3d3d;
22 padding: 0 5px;
23 font-size: 13px;
24 margin-bottom: 5px;
25 font-weight: $font-semibold;
26 border-radius: 3px;
27 }
28
18 .comment-account-date { 29 .comment-account-date {
19 display: flex; 30 display: flex;
20 margin-bottom: 4px; 31 margin-bottom: 4px;
@@ -32,13 +43,6 @@
32 } 43 }
33 } 44 }
34 45
35 .marked-comment {
36 background-color: #F5F5F5;
37 padding-left: 3px;
38 padding-right: 3px;
39 font-size: 12px;
40 }
41
42 .comment-html { 46 .comment-html {
43 a { 47 a {
44 @include disable-default-a-behaviour; 48 @include disable-default-a-behaviour;
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 38e603d0d..7c664ca60 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,4 +1,4 @@
1import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' 1import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'
2import * as sanitizeHtml from 'sanitize-html' 2import * as sanitizeHtml from 'sanitize-html'
3import { Account as AccountInterface } from '../../../../../../shared/models/actors' 3import { Account as AccountInterface } from '../../../../../../shared/models/actors'
4import { UserRight } from '../../../../../../shared/models/users' 4import { UserRight } from '../../../../../../shared/models/users'
@@ -13,12 +13,13 @@ import { VideoComment } from './video-comment.model'
13 templateUrl: './video-comment.component.html', 13 templateUrl: './video-comment.component.html',
14 styleUrls: ['./video-comment.component.scss'] 14 styleUrls: ['./video-comment.component.scss']
15}) 15})
16export class VideoCommentComponent implements OnInit { 16export class VideoCommentComponent implements OnInit, OnChanges {
17 @Input() video: Video 17 @Input() video: Video
18 @Input() comment: VideoComment 18 @Input() comment: VideoComment
19 @Input() parentComments: VideoComment[] = [] 19 @Input() parentComments: VideoComment[] = []
20 @Input() commentTree: VideoCommentThreadTree 20 @Input() commentTree: VideoCommentThreadTree
21 @Input() inReplyToCommentId: number 21 @Input() inReplyToCommentId: number
22 @Input() highlightedComment = false
22 23
23 @Output() wantedToDelete = new EventEmitter<VideoComment>() 24 @Output() wantedToDelete = new EventEmitter<VideoComment>()
24 @Output() wantedToReply = new EventEmitter<VideoComment>() 25 @Output() wantedToReply = new EventEmitter<VideoComment>()
@@ -35,11 +36,11 @@ export class VideoCommentComponent implements OnInit {
35 } 36 }
36 37
37 ngOnInit () { 38 ngOnInit () {
38 this.sanitizedCommentHTML = sanitizeHtml(this.comment.text, { 39 this.init()
39 allowedTags: [ 'p', 'span' ] 40 }
40 })
41 41
42 this.newParentComments = this.parentComments.concat([ this.comment ]) 42 ngOnChanges () {
43 this.init()
43 } 44 }
44 45
45 onCommentReplyCreated (createdComment: VideoComment) { 46 onCommentReplyCreated (createdComment: VideoComment) {
@@ -86,4 +87,12 @@ export class VideoCommentComponent implements OnInit {
86 this.user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) 87 this.user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)
87 ) 88 )
88 } 89 }
90
91 private init () {
92 this.sanitizedCommentHTML = sanitizeHtml(this.comment.text, {
93 allowedTags: [ 'p', 'span' ]
94 })
95
96 this.newParentComments = this.parentComments.concat([ this.comment ])
97 }
89} 98}
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 d7b03521a..8fa02aee1 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,6 @@ export class VideoComment implements VideoCommentServerModel {
14 account: AccountInterface 14 account: AccountInterface
15 totalReplies: number 15 totalReplies: number
16 by: string 16 by: string
17 marked = false
18 17
19 constructor (hash: VideoCommentServerModel) { 18 constructor (hash: VideoCommentServerModel) {
20 this.id = hash.id 19 this.id = hash.id
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 7f2e96e93..f95e2cbba 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
@@ -19,8 +19,23 @@
19 [autoLoading]="true" 19 [autoLoading]="true"
20 (nearOfBottom)="onNearOfBottom()" 20 (nearOfBottom)="onNearOfBottom()"
21 > 21 >
22 <div *ngIf="highlightedComment" id="highlighted-comment">
23 <my-video-comment
24 [comment]="highlightedComment"
25 [video]="video"
26 [inReplyToCommentId]="inReplyToCommentId"
27 [commentTree]="threadComments[highlightedComment.id]"
28 [highlightedComment]="true"
29 (wantedToReply)="onWantedToReply($event)"
30 (wantedToDelete)="onWantedToDelete($event)"
31 (threadCreated)="onThreadCreated($event)"
32 (resetReply)="onResetReply()"
33 ></my-video-comment>
34 </div>
35
22 <div *ngFor="let comment of comments"> 36 <div *ngFor="let comment of comments">
23 <my-video-comment 37 <my-video-comment
38 *ngIf="!highlightedComment || comment.id !== highlightedComment.id"
24 [comment]="comment" 39 [comment]="comment"
25 [video]="video" 40 [video]="video"
26 [inReplyToCommentId]="inReplyToCommentId" 41 [inReplyToCommentId]="inReplyToCommentId"
@@ -31,7 +46,7 @@
31 (resetReply)="onResetReply()" 46 (resetReply)="onResetReply()"
32 ></my-video-comment> 47 ></my-video-comment>
33 48
34 <div *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment)" class="view-replies"> 49 <div *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment.id)" class="view-replies">
35 View all {{ comment.totalReplies }} replies 50 View all {{ comment.totalReplies }} replies
36 51
37 <span *ngIf="!threadLoading[comment.id]" class="glyphicon glyphicon-menu-down"></span> 52 <span *ngIf="!threadLoading[comment.id]" class="glyphicon glyphicon-menu-down"></span>
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 19ab3b633..bf8b154a2 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
@@ -1,6 +1,10 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4#highlighted-comment {
5 margin-bottom: 25px;
6}
7
4.view-replies { 8.view-replies {
5 font-weight: $font-semibold; 9 font-weight: $font-semibold;
6 font-size: 15px; 10 font-size: 15px;
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 7970a5dcf..aada9554d 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,7 +1,9 @@
1import { Component, Input, OnChanges, SimpleChanges } from '@angular/core' 1import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'
2import { ActivatedRoute } from '@angular/router'
2import { ConfirmService } from '@app/core' 3import { ConfirmService } from '@app/core'
3import { NotificationsService } from 'angular2-notifications' 4import { NotificationsService } from 'angular2-notifications'
4import { VideoComment as VideoCommentInterface, VideoCommentThreadTree } from '../../../../../../shared/models/videos/video-comment.model' 5import { Subscription } from 'rxjs/Subscription'
6import { VideoCommentThreadTree } from '../../../../../../shared/models/videos/video-comment.model'
5import { AuthService } from '../../../core/auth' 7import { AuthService } from '../../../core/auth'
6import { ComponentPagination } from '../../../shared/rest/component-pagination.model' 8import { ComponentPagination } from '../../../shared/rest/component-pagination.model'
7import { User } from '../../../shared/users' 9import { User } from '../../../shared/users'
@@ -9,18 +11,18 @@ import { SortField } from '../../../shared/video/sort-field.type'
9import { VideoDetails } from '../../../shared/video/video-details.model' 11import { VideoDetails } from '../../../shared/video/video-details.model'
10import { VideoComment } from './video-comment.model' 12import { VideoComment } from './video-comment.model'
11import { VideoCommentService } from './video-comment.service' 13import { VideoCommentService } from './video-comment.service'
12import { ActivatedRoute } from '@angular/router'
13 14
14@Component({ 15@Component({
15 selector: 'my-video-comments', 16 selector: 'my-video-comments',
16 templateUrl: './video-comments.component.html', 17 templateUrl: './video-comments.component.html',
17 styleUrls: ['./video-comments.component.scss'] 18 styleUrls: ['./video-comments.component.scss']
18}) 19})
19export class VideoCommentsComponent implements OnChanges { 20export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
20 @Input() video: VideoDetails 21 @Input() video: VideoDetails
21 @Input() user: User 22 @Input() user: User
22 23
23 comments: VideoComment[] = [] 24 comments: VideoComment[] = []
25 highlightedComment: VideoComment
24 sort: SortField = '-createdAt' 26 sort: SortField = '-createdAt'
25 componentPagination: ComponentPagination = { 27 componentPagination: ComponentPagination = {
26 currentPage: 1, 28 currentPage: 1,
@@ -30,7 +32,8 @@ export class VideoCommentsComponent implements OnChanges {
30 inReplyToCommentId: number 32 inReplyToCommentId: number
31 threadComments: { [ id: number ]: VideoCommentThreadTree } = {} 33 threadComments: { [ id: number ]: VideoCommentThreadTree } = {}
32 threadLoading: { [ id: number ]: boolean } = {} 34 threadLoading: { [ id: number ]: boolean } = {}
33 markedCommentID: number 35
36 private sub: Subscription
34 37
35 constructor ( 38 constructor (
36 private authService: AuthService, 39 private authService: AuthService,
@@ -40,20 +43,38 @@ export class VideoCommentsComponent implements OnChanges {
40 private activatedRoute: ActivatedRoute 43 private activatedRoute: ActivatedRoute
41 ) {} 44 ) {}
42 45
46 ngOnInit () {
47 // Find highlighted comment in params
48 this.sub = this.activatedRoute.params.subscribe(
49 params => {
50 if (params['commentId']) {
51 const highlightedCommentId = +params['commentId']
52 this.processHighlightedComment(highlightedCommentId)
53 }
54 }
55 )
56 }
57
43 ngOnChanges (changes: SimpleChanges) { 58 ngOnChanges (changes: SimpleChanges) {
44 if (changes['video']) { 59 if (changes['video']) {
45 this.loadVideoComments() 60 this.resetVideo()
46 } 61 }
47 } 62 }
48 63
49 viewReplies (comment: VideoCommentInterface) { 64 ngOnDestroy () {
50 this.threadLoading[comment.id] = true 65 if (this.sub) this.sub.unsubscribe()
66 }
67
68 viewReplies (commentId: number, highlightComment = false) {
69 this.threadLoading[commentId] = true
51 70
52 this.videoCommentService.getVideoThreadComments(this.video.id, comment.id) 71 this.videoCommentService.getVideoThreadComments(this.video.id, commentId)
53 .subscribe( 72 .subscribe(
54 res => { 73 res => {
55 this.threadComments[comment.id] = res 74 this.threadComments[commentId] = res
56 this.threadLoading[comment.id] = false 75 this.threadLoading[commentId] = false
76
77 if (highlightComment) this.highlightedComment = new VideoComment(res.comment)
57 }, 78 },
58 79
59 err => this.notificationsService.error('Error', err.message) 80 err => this.notificationsService.error('Error', err.message)
@@ -66,18 +87,6 @@ export class VideoCommentsComponent implements OnChanges {
66 res => { 87 res => {
67 this.comments = this.comments.concat(res.comments) 88 this.comments = this.comments.concat(res.comments)
68 this.componentPagination.totalItems = res.totalComments 89 this.componentPagination.totalItems = res.totalComments
69
70 if (this.markedCommentID) {
71 // If there is a marked comment, retrieve it separately as it may not be on this page, filter to prevent duplicate
72 this.comments = this.comments.filter(value => value.id !== this.markedCommentID)
73 this.videoCommentService.getVideoThreadComments(this.video.id, this.markedCommentID).subscribe(
74 res => {
75 let comment = new VideoComment(res.comment)
76 comment.marked = true
77 this.comments.unshift(comment) // Insert marked comment at the beginning
78 }
79 )
80 }
81 }, 90 },
82 91
83 err => this.notificationsService.error('Error', err.message) 92 err => this.notificationsService.error('Error', err.message)
@@ -97,7 +106,7 @@ export class VideoCommentsComponent implements OnChanges {
97 } 106 }
98 107
99 onThreadCreated (commentTree: VideoCommentThreadTree) { 108 onThreadCreated (commentTree: VideoCommentThreadTree) {
100 this.viewReplies(commentTree.comment) 109 this.viewReplies(commentTree.comment.id)
101 } 110 }
102 111
103 onWantedToDelete (commentToDelete: VideoComment) { 112 onWantedToDelete (commentToDelete: VideoComment) {
@@ -168,9 +177,10 @@ export class VideoCommentsComponent implements OnChanges {
168 } 177 }
169 } 178 }
170 179
171 private loadVideoComments () { 180 private resetVideo () {
172 if (this.video.commentsEnabled === true) { 181 if (this.video.commentsEnabled === true) {
173 // Reset all our fields 182 // Reset all our fields
183 this.highlightedComment = null
174 this.comments = [] 184 this.comments = []
175 this.threadComments = {} 185 this.threadComments = {}
176 this.threadLoading = {} 186 this.threadLoading = {}
@@ -178,16 +188,14 @@ export class VideoCommentsComponent implements OnChanges {
178 this.componentPagination.currentPage = 1 188 this.componentPagination.currentPage = 1
179 this.componentPagination.totalItems = null 189 this.componentPagination.totalItems = null
180 190
181 // Find marked comment in params
182 this.activatedRoute.params.subscribe(
183 params => {
184 if (params['commentId']) {
185 this.markedCommentID = +params['commentId']
186 }
187 }
188 )
189
190 this.loadMoreComments() 191 this.loadMoreComments()
191 } 192 }
192 } 193 }
194
195 private processHighlightedComment (highlightedCommentId: number) {
196 this.highlightedComment = this.comments.find(c => c.id === highlightedCommentId)
197
198 const highlightComment = true
199 this.viewReplies(highlightedCommentId, highlightComment)
200 }
193} 201}
diff --git a/client/src/app/videos/+video-watch/video-watch-routing.module.ts b/client/src/app/videos/+video-watch/video-watch-routing.module.ts
index 72f76ab46..bdd4f945e 100644
--- a/client/src/app/videos/+video-watch/video-watch-routing.module.ts
+++ b/client/src/app/videos/+video-watch/video-watch-routing.module.ts
@@ -10,11 +10,6 @@ const videoWatchRoutes: Routes = [
10 path: '', 10 path: '',
11 component: VideoWatchComponent, 11 component: VideoWatchComponent,
12 canActivate: [ MetaGuard ] 12 canActivate: [ MetaGuard ]
13 },
14 {
15 path: 'comment/:commentId',
16 component: VideoWatchComponent,
17 canActivate: [ MetaGuard ]
18 } 13 }
19] 14]
20 15
diff --git a/client/src/app/videos/+video-watch/video-watch.component.ts b/client/src/app/videos/+video-watch/video-watch.component.ts
index 7c97f0964..553eed341 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -83,7 +83,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
83 this.player.pause() 83 this.player.pause()
84 } 84 }
85 85
86 let uuid = routeParams['uuid'] 86 const uuid = routeParams['uuid']
87 // Video did not changed
88 if (this.video && this.video.uuid === uuid) return
89
87 this.videoService.getVideo(uuid).subscribe( 90 this.videoService.getVideo(uuid).subscribe(
88 video => this.onVideoFetched(video), 91 video => this.onVideoFetched(video),
89 92