aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src
diff options
context:
space:
mode:
Diffstat (limited to 'client/src')
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss1
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment-add.component.ts30
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.html4
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.scss3
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.ts15
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comments.component.html5
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comments.component.ts25
7 files changed, 73 insertions, 10 deletions
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
index b3725ab94..d8f851baf 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
+++ b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss
@@ -22,6 +22,7 @@ form {
22 22
23 textarea { 23 textarea {
24 @include peertube-textarea(100%, 60px); 24 @include peertube-textarea(100%, 60px);
25 @include button-focus(pvar(--mainColorLightest));
25 26
26 &:focus::placeholder { 27 &:focus::placeholder {
27 opacity: 0; 28 opacity: 0;
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 d79efbb49..c1ddc0695 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,5 +1,5 @@
1import { Observable } from 'rxjs' 1import { Observable } from 'rxjs'
2import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' 2import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4import { Notifier, User } from '@app/core' 4import { Notifier, User } from '@app/core'
5import { FormReactive, FormValidatorService, VideoCommentValidatorsService } from '@app/shared/shared-forms' 5import { FormReactive, FormValidatorService, VideoCommentValidatorsService } from '@app/shared/shared-forms'
@@ -13,12 +13,13 @@ import { VideoCommentCreate } from '@shared/models'
13 templateUrl: './video-comment-add.component.html', 13 templateUrl: './video-comment-add.component.html',
14 styleUrls: ['./video-comment-add.component.scss'] 14 styleUrls: ['./video-comment-add.component.scss']
15}) 15})
16export class VideoCommentAddComponent extends FormReactive implements OnInit { 16export class VideoCommentAddComponent extends FormReactive implements OnChanges, OnInit {
17 @Input() user: User 17 @Input() user: User
18 @Input() video: Video 18 @Input() video: Video
19 @Input() parentComment: VideoComment 19 @Input() parentComment: VideoComment
20 @Input() parentComments: VideoComment[] 20 @Input() parentComments: VideoComment[]
21 @Input() focusOnInit = false 21 @Input() focusOnInit = false
22 @Input() textValue?: string
22 23
23 @Output() commentCreated = new EventEmitter<VideoComment>() 24 @Output() commentCreated = new EventEmitter<VideoComment>()
24 @Output() cancel = new EventEmitter() 25 @Output() cancel = new EventEmitter()
@@ -45,8 +46,9 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
45 }) 46 })
46 47
47 if (this.user) { 48 if (this.user) {
48 if (this.focusOnInit === true) { 49 if (this.textValue) {
49 this.textareaElement.nativeElement.focus() 50 this.patchTextValue(this.textValue, this.focusOnInit)
51 return
50 } 52 }
51 53
52 if (this.parentComment) { 54 if (this.parentComment) {
@@ -57,11 +59,17 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
57 const mentionsSet = new Set(mentions) 59 const mentionsSet = new Set(mentions)
58 const mentionsText = Array.from(mentionsSet).join(' ') + ' ' 60 const mentionsText = Array.from(mentionsSet).join(' ') + ' '
59 61
60 this.form.patchValue({ text: mentionsText }) 62 this.patchTextValue(mentionsText, this.focusOnInit)
61 } 63 }
62 } 64 }
63 } 65 }
64 66
67 ngOnChanges (changes: SimpleChanges) {
68 if (changes.textValue && changes.textValue.currentValue && changes.textValue.currentValue !== changes.textValue.previousValue) {
69 this.patchTextValue(changes.textValue.currentValue, true)
70 }
71 }
72
65 onValidKey () { 73 onValidKey () {
66 this.check() 74 this.check()
67 if (!this.form.valid) return 75 if (!this.form.valid) return
@@ -145,4 +153,16 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
145 return this.videoCommentService 153 return this.videoCommentService
146 .addCommentThread(this.video.id, commentCreate) 154 .addCommentThread(this.video.id, commentCreate)
147 } 155 }
156
157 private patchTextValue (text: string, focus: boolean) {
158 setTimeout(() => {
159 if (focus) {
160 this.textareaElement.nativeElement.focus()
161 }
162
163 this.textareaElement.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' })
164 })
165
166 this.form.patchValue({ text })
167 }
148} 168}
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 aa6d45789..fdb555d18 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
@@ -43,6 +43,7 @@
43 <div class="comment-actions"> 43 <div class="comment-actions">
44 <div *ngIf="isUserLoggedIn()" (click)="onWantToReply()" class="comment-action-reply" i18n>Reply</div> 44 <div *ngIf="isUserLoggedIn()" (click)="onWantToReply()" class="comment-action-reply" i18n>Reply</div>
45 <div *ngIf="isRemovableByUser()" (click)="onWantToDelete()" class="comment-action-delete" i18n>Delete</div> 45 <div *ngIf="isRemovableByUser()" (click)="onWantToDelete()" class="comment-action-delete" i18n>Delete</div>
46 <div *ngIf="isRedraftableByUser()" (click)="onWantToRedraft()" class="comment-action-redraft" i18n>Delete & re-draft</div>
46 47
47 <my-user-moderation-dropdown 48 <my-user-moderation-dropdown
48 [prependActions]="prependModerationActions" 49 [prependActions]="prependModerationActions"
@@ -72,6 +73,7 @@
72 [focusOnInit]="true" 73 [focusOnInit]="true"
73 (commentCreated)="onCommentReplyCreated($event)" 74 (commentCreated)="onCommentReplyCreated($event)"
74 (cancel)="onResetReply()" 75 (cancel)="onResetReply()"
76 [textValue]="redraftValue"
75 ></my-video-comment-add> 77 ></my-video-comment-add>
76 78
77 <div *ngIf="commentTree" class="children"> 79 <div *ngIf="commentTree" class="children">
@@ -84,8 +86,10 @@
84 [parentComments]="newParentComments" 86 [parentComments]="newParentComments"
85 (wantedToReply)="onWantToReply($event)" 87 (wantedToReply)="onWantToReply($event)"
86 (wantedToDelete)="onWantToDelete($event)" 88 (wantedToDelete)="onWantToDelete($event)"
89 (wantedToRedraft)="onWantToRedraft($event)"
87 (resetReply)="onResetReply()" 90 (resetReply)="onResetReply()"
88 (timestampClicked)="handleTimestampClicked($event)" 91 (timestampClicked)="handleTimestampClicked($event)"
92 [redraftValue]="redraftValue"
89 ></my-video-comment> 93 ></my-video-comment>
90 </div> 94 </div>
91 </div> 95 </div>
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 e7ef79561..151e3ba3a 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
@@ -119,7 +119,8 @@
119 119
120 ::ng-deep .dropdown-toggle, 120 ::ng-deep .dropdown-toggle,
121 .comment-action-reply, 121 .comment-action-reply,
122 .comment-action-delete { 122 .comment-action-delete,
123 .comment-action-redraft {
123 color: pvar(--greyForegroundColor); 124 color: pvar(--greyForegroundColor);
124 cursor: pointer; 125 cursor: pointer;
125 margin-right: 10px; 126 margin-right: 10px;
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 a84e91fd3..507132909 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
@@ -22,9 +22,11 @@ export class VideoCommentComponent implements OnInit, OnChanges {
22 @Input() inReplyToCommentId: number 22 @Input() inReplyToCommentId: number
23 @Input() highlightedComment = false 23 @Input() highlightedComment = false
24 @Input() firstInThread = false 24 @Input() firstInThread = false
25 @Input() redraftValue?: string
25 26
26 @Output() wantedToDelete = new EventEmitter<VideoComment>()
27 @Output() wantedToReply = new EventEmitter<VideoComment>() 27 @Output() wantedToReply = new EventEmitter<VideoComment>()
28 @Output() wantedToDelete = new EventEmitter<VideoComment>()
29 @Output() wantedToRedraft = new EventEmitter<VideoComment>()
28 @Output() threadCreated = new EventEmitter<VideoCommentThreadTree>() 30 @Output() threadCreated = new EventEmitter<VideoCommentThreadTree>()
29 @Output() resetReply = new EventEmitter() 31 @Output() resetReply = new EventEmitter()
30 @Output() timestampClicked = new EventEmitter<number>() 32 @Output() timestampClicked = new EventEmitter<number>()
@@ -70,7 +72,10 @@ export class VideoCommentComponent implements OnInit, OnChanges {
70 comment: createdComment, 72 comment: createdComment,
71 children: [] 73 children: []
72 }) 74 })
75
73 this.resetReply.emit() 76 this.resetReply.emit()
77
78 delete this.redraftValue
74 } 79 }
75 80
76 onWantToReply (comment?: VideoComment) { 81 onWantToReply (comment?: VideoComment) {
@@ -81,6 +86,10 @@ export class VideoCommentComponent implements OnInit, OnChanges {
81 this.wantedToDelete.emit(comment || this.comment) 86 this.wantedToDelete.emit(comment || this.comment)
82 } 87 }
83 88
89 onWantToRedraft (comment?: VideoComment) {
90 this.wantedToRedraft.emit(comment || this.comment)
91 }
92
84 isUserLoggedIn () { 93 isUserLoggedIn () {
85 return this.authService.isLoggedIn() 94 return this.authService.isLoggedIn()
86 } 95 }
@@ -102,6 +111,10 @@ export class VideoCommentComponent implements OnInit, OnChanges {
102 ) 111 )
103 } 112 }
104 113
114 isRedraftableByUser () {
115 return this.comment.account && this.isUserLoggedIn() && this.user.account.id === this.comment.account.id && this.comment.totalReplies === 0
116 }
117
105 switchToDefaultAvatar ($event: Event) { 118 switchToDefaultAvatar ($event: Event) {
106 ($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL() 119 ($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL()
107 } 120 }
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 dd1d43560..1bc0885a4 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
@@ -27,6 +27,7 @@
27 [video]="video" 27 [video]="video"
28 [user]="user" 28 [user]="user"
29 (commentCreated)="onCommentThreadCreated($event)" 29 (commentCreated)="onCommentThreadCreated($event)"
30 [textValue]="commentThreadRedraftValue"
30 ></my-video-comment-add> 31 ></my-video-comment-add>
31 32
32 <div *ngIf="componentPagination.totalItems === 0 && comments.length === 0" i18n>No comments.</div> 33 <div *ngIf="componentPagination.totalItems === 0 && comments.length === 0" i18n>No comments.</div>
@@ -50,9 +51,11 @@
50 [firstInThread]="true" 51 [firstInThread]="true"
51 (wantedToReply)="onWantedToReply($event)" 52 (wantedToReply)="onWantedToReply($event)"
52 (wantedToDelete)="onWantedToDelete($event)" 53 (wantedToDelete)="onWantedToDelete($event)"
54 (wantedToRedraft)="onWantedToRedraft($event)"
53 (threadCreated)="onThreadCreated($event)" 55 (threadCreated)="onThreadCreated($event)"
54 (resetReply)="onResetReply()" 56 (resetReply)="onResetReply()"
55 (timestampClicked)="handleTimestampClicked($event)" 57 (timestampClicked)="handleTimestampClicked($event)"
58 [redraftValue]="commentReplyRedraftValue"
56 ></my-video-comment> 59 ></my-video-comment>
57 </div> 60 </div>
58 61
@@ -66,9 +69,11 @@
66 [firstInThread]="i + 1 !== comments.length" 69 [firstInThread]="i + 1 !== comments.length"
67 (wantedToReply)="onWantedToReply($event)" 70 (wantedToReply)="onWantedToReply($event)"
68 (wantedToDelete)="onWantedToDelete($event)" 71 (wantedToDelete)="onWantedToDelete($event)"
72 (wantedToRedraft)="onWantedToRedraft($event)"
69 (threadCreated)="onThreadCreated($event)" 73 (threadCreated)="onThreadCreated($event)"
70 (resetReply)="onResetReply()" 74 (resetReply)="onResetReply()"
71 (timestampClicked)="handleTimestampClicked($event)" 75 (timestampClicked)="handleTimestampClicked($event)"
76 [redraftValue]="commentReplyRedraftValue"
72 > 77 >
73 <div *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment.id)" class="view-replies mb-2"> 78 <div *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment.id)" class="view-replies mb-2">
74 <span class="glyphicon glyphicon-menu-down"></span> 79 <span class="glyphicon glyphicon-menu-down"></span>
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 517844ab2..8080afd8d 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
@@ -27,6 +27,8 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
27 totalItems: null 27 totalItems: null
28 } 28 }
29 inReplyToCommentId: number 29 inReplyToCommentId: number
30 commentReplyRedraftValue: string
31 commentThreadRedraftValue: string
30 threadComments: { [ id: number ]: VideoCommentThreadTree } = {} 32 threadComments: { [ id: number ]: VideoCommentThreadTree } = {}
31 threadLoading: { [ id: number ]: boolean } = {} 33 threadLoading: { [ id: number ]: boolean } = {}
32 34
@@ -131,6 +133,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
131 133
132 onCommentThreadCreated (comment: VideoComment) { 134 onCommentThreadCreated (comment: VideoComment) {
133 this.comments.unshift(comment) 135 this.comments.unshift(comment)
136 delete this.commentThreadRedraftValue
134 } 137 }
135 138
136 onWantedToReply (comment: VideoComment) { 139 onWantedToReply (comment: VideoComment) {
@@ -139,6 +142,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
139 142
140 onResetReply () { 143 onResetReply () {
141 this.inReplyToCommentId = undefined 144 this.inReplyToCommentId = undefined
145 delete this.commentReplyRedraftValue
142 } 146 }
143 147
144 onThreadCreated (commentTree: VideoCommentThreadTree) { 148 onThreadCreated (commentTree: VideoCommentThreadTree) {
@@ -156,9 +160,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
156 this.timestampClicked.emit(timestamp) 160 this.timestampClicked.emit(timestamp)
157 } 161 }
158 162
159 async onWantedToDelete (commentToDelete: VideoComment) { 163 async onWantedToDelete (commentToDelete: VideoComment, message = 'Do you really want to delete this comment?'): Promise<boolean> {
160 let message = 'Do you really want to delete this comment?'
161
162 if (commentToDelete.isLocal || this.video.isLocal) { 164 if (commentToDelete.isLocal || this.video.isLocal) {
163 message += $localize` The deletion will be sent to remote instances so they can reflect the change.` 165 message += $localize` The deletion will be sent to remote instances so they can reflect the change.`
164 } else { 166 } else {
@@ -183,6 +185,23 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
183 185
184 err => this.notifier.error(err.message) 186 err => this.notifier.error(err.message)
185 ) 187 )
188
189 return true
190 }
191
192 async onWantedToRedraft(commentToRedraft: VideoComment) {
193 const confirm = await this.onWantedToDelete(commentToRedraft, 'Do you really want to delete and re-draft this comment?')
194
195 if (confirm) {
196 this.inReplyToCommentId = commentToRedraft.inReplyToCommentId
197
198 if (commentToRedraft.threadId === commentToRedraft.id) {
199 this.commentThreadRedraftValue = commentToRedraft.text
200 } else {
201 this.commentReplyRedraftValue = commentToRedraft.text
202 }
203
204 }
186 } 205 }
187 206
188 isUserLoggedIn () { 207 isUserLoggedIn () {