diff options
author | kimsible <kimsible@users.noreply.github.com> | 2020-08-07 14:03:28 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-08-14 15:03:38 +0200 |
commit | f63c03fb6ecccb5ac8e0d88917f072339b38ffb5 (patch) | |
tree | 37d9dc9fd18057e5071cfc7bba51177d02d8c094 | |
parent | 09f8f73fbc8177930c76171300d4abc9f380d746 (diff) | |
download | PeerTube-f63c03fb6ecccb5ac8e0d88917f072339b38ffb5.tar.gz PeerTube-f63c03fb6ecccb5ac8e0d88917f072339b38ffb5.tar.zst PeerTube-f63c03fb6ecccb5ac8e0d88917f072339b38ffb5.zip |
Add delete & re-draft for comments without replies
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 @@ | |||
1 | import { Observable } from 'rxjs' | 1 | import { Observable } from 'rxjs' |
2 | import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' | 2 | import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core' |
3 | import { Router } from '@angular/router' | 3 | import { Router } from '@angular/router' |
4 | import { Notifier, User } from '@app/core' | 4 | import { Notifier, User } from '@app/core' |
5 | import { FormReactive, FormValidatorService, VideoCommentValidatorsService } from '@app/shared/shared-forms' | 5 | import { 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 | }) |
16 | export class VideoCommentAddComponent extends FormReactive implements OnInit { | 16 | export 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 () { |