]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
Refactor form reactive
[github/Chocobozzz/PeerTube.git] / client / src / app / shared / shared-abuse-list / abuse-list-table.component.ts
CommitLineData
94148c90
C
1import * as debug from 'debug'
2import truncate from 'lodash-es/truncate'
3import { SortMeta } from 'primeng/api'
2e46eb97 4import { Component, Input, OnInit, ViewChild } from '@angular/core'
94148c90 5import { DomSanitizer } from '@angular/platform-browser'
5ed46c1b 6import { ActivatedRoute, Router } from '@angular/router'
94148c90
C
7import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core'
8import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main'
9import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
10import { VideoCommentService } from '@app/shared/shared-video-comment'
42b40636 11import { logger } from '@root-helpers/logger'
94148c90 12import { AbuseState, AdminAbuse } from '@shared/models'
2e46eb97 13import { AdvancedInputFilter } from '../shared-forms'
94148c90
C
14import { AbuseMessageModalComponent } from './abuse-message-modal.component'
15import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
16import { ProcessedAbuse } from './processed-abuse.model'
17
42b40636 18const debugLogger = debug('peertube:moderation:AbuseListTableComponent')
94148c90
C
19
20@Component({
21 selector: 'my-abuse-list-table',
22 templateUrl: './abuse-list-table.component.html',
23 styleUrls: [ '../shared-moderation/moderation.scss', './abuse-list-table.component.scss' ]
24})
2e46eb97 25export class AbuseListTableComponent extends RestTable implements OnInit {
94148c90 26 @Input() viewType: 'admin' | 'user'
94148c90
C
27
28 @ViewChild('abuseMessagesModal', { static: true }) abuseMessagesModal: AbuseMessageModalComponent
29 @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent
30
31 abuses: ProcessedAbuse[] = []
32 totalRecords = 0
33 sort: SortMeta = { field: 'createdAt', order: 1 }
34 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
35
36 abuseActions: DropdownAction<ProcessedAbuse>[][] = []
37
1fd61899
C
38 inputFilters: AdvancedInputFilter[] = [
39 {
978c87e7
C
40 title: $localize`Advanced filters`,
41 children: [
42 {
dd6d2a7c 43 value: 'state:pending',
978c87e7
C
44 label: $localize`Unsolved reports`
45 },
46 {
dd6d2a7c 47 value: 'state:accepted',
978c87e7
C
48 label: $localize`Accepted reports`
49 },
50 {
dd6d2a7c 51 value: 'state:rejected',
978c87e7
C
52 label: $localize`Refused reports`
53 },
54 {
dd6d2a7c 55 value: 'videoIs:blacklisted',
978c87e7
C
56 label: $localize`Reports with blocked videos`
57 },
58 {
dd6d2a7c 59 value: 'videoIs:deleted',
978c87e7
C
60 label: $localize`Reports with deleted videos`
61 }
62 ]
1fd61899
C
63 }
64 ]
65
94148c90 66 constructor (
5ed46c1b
C
67 protected route: ActivatedRoute,
68 protected router: Router,
94148c90
C
69 private notifier: Notifier,
70 private abuseService: AbuseService,
71 private blocklistService: BlocklistService,
72 private commentService: VideoCommentService,
73 private videoService: VideoService,
74 private videoBlocklistService: VideoBlockService,
75 private confirmService: ConfirmService,
94148c90 76 private markdownRenderer: MarkdownService,
5ed46c1b 77 private sanitizer: DomSanitizer
94148c90
C
78 ) {
79 super()
80 }
81
82 ngOnInit () {
83 this.abuseActions = [
84 this.buildInternalActions(),
85
86 this.buildFlaggedAccountActions(),
87
88 this.buildCommentActions(),
89
90 this.buildVideoActions(),
91
92 this.buildAccountActions()
93 ]
94
95 this.initialize()
94148c90
C
96 }
97
98 isAdminView () {
99 return this.viewType === 'admin'
100 }
101
102 getIdentifier () {
103 return 'AbuseListTableComponent'
104 }
105
106 openModerationCommentModal (abuse: AdminAbuse) {
107 this.moderationCommentModal.openModal(abuse)
108 }
109
110 onModerationCommentUpdated () {
2e46eb97 111 this.reloadData()
94148c90
C
112 }
113
94148c90
C
114 isAbuseAccepted (abuse: AdminAbuse) {
115 return abuse.state.id === AbuseState.ACCEPTED
116 }
117
118 isAbuseRejected (abuse: AdminAbuse) {
119 return abuse.state.id === AbuseState.REJECTED
120 }
121
122 getVideoUrl (abuse: AdminAbuse) {
d4a8e7a6 123 return Video.buildWatchUrl(abuse.video)
94148c90
C
124 }
125
126 getCommentUrl (abuse: AdminAbuse) {
d4a8e7a6 127 return Video.buildWatchUrl(abuse.comment.video) + ';threadId=' + abuse.comment.threadId
94148c90
C
128 }
129
130 getAccountUrl (abuse: ProcessedAbuse) {
71887396 131 return '/a/' + abuse.flaggedAccount.nameWithHost
94148c90
C
132 }
133
94148c90 134 async removeAbuse (abuse: AdminAbuse) {
66357162 135 const res = await this.confirmService.confirm($localize`Do you really want to delete this abuse report?`, $localize`Delete`)
94148c90
C
136 if (res === false) return
137
1378c0d3
C
138 this.abuseService.removeAbuse(abuse)
139 .subscribe({
140 next: () => {
141 this.notifier.success($localize`Abuse deleted.`)
142 this.reloadData()
143 },
94148c90 144
1378c0d3
C
145 error: err => this.notifier.error(err.message)
146 })
94148c90
C
147 }
148
149 updateAbuseState (abuse: AdminAbuse, state: AbuseState) {
150 this.abuseService.updateAbuse(abuse, { state })
1378c0d3
C
151 .subscribe({
152 next: () => this.reloadData(),
94148c90 153
1378c0d3
C
154 error: err => this.notifier.error(err.message)
155 })
94148c90
C
156 }
157
158 onCountMessagesUpdated (event: { abuseId: number, countMessages: number }) {
159 const abuse = this.abuses.find(a => a.id === event.abuseId)
160
161 if (!abuse) {
42b40636 162 logger.error(`Cannot find abuse ${event.abuseId}`)
94148c90
C
163 return
164 }
165
166 abuse.countMessages = event.countMessages
167 }
168
169 openAbuseMessagesModal (abuse: AdminAbuse) {
170 this.abuseMessagesModal.openModal(abuse)
171 }
172
173 isLocalAbuse (abuse: AdminAbuse) {
174 if (this.viewType === 'user') return true
82b9a100 175 if (!abuse.reporterAccount) return false
94148c90
C
176
177 return Actor.IS_LOCAL(abuse.reporterAccount.host)
178 }
179
2e46eb97 180 protected reloadData () {
42b40636 181 debugLogger('Loading data.')
94148c90
C
182
183 const options = {
184 pagination: this.pagination,
185 sort: this.sort,
186 search: this.search
187 }
188
189 const observable = this.viewType === 'admin'
190 ? this.abuseService.getAdminAbuses(options)
191 : this.abuseService.getUserAbuses(options)
192
1378c0d3
C
193 return observable.subscribe({
194 next: async resultList => {
195 this.totalRecords = resultList.total
94148c90 196
1378c0d3 197 this.abuses = []
94148c90 198
1378c0d3
C
199 for (const a of resultList.data) {
200 const abuse = a as ProcessedAbuse
94148c90 201
1378c0d3 202 abuse.reasonHtml = await this.toHtml(abuse.reason)
94148c90 203
1378c0d3
C
204 if (abuse.moderationComment) {
205 abuse.moderationCommentHtml = await this.toHtml(abuse.moderationComment)
206 }
94148c90 207
1378c0d3 208 if (abuse.video) {
1378c0d3
C
209 if (abuse.video.channel?.ownerAccount) {
210 abuse.video.channel.ownerAccount = new Account(abuse.video.channel.ownerAccount)
94148c90 211 }
1378c0d3 212 }
94148c90 213
1378c0d3
C
214 if (abuse.comment) {
215 if (abuse.comment.deleted) {
216 abuse.truncatedCommentHtml = abuse.commentHtml = $localize`Deleted comment`
217 } else {
218 const truncated = truncate(abuse.comment.text, { length: 100 })
219 abuse.truncatedCommentHtml = await this.markdownRenderer.textMarkdownToHTML(truncated, true)
220 abuse.commentHtml = await this.markdownRenderer.textMarkdownToHTML(abuse.comment.text, true)
94148c90 221 }
1378c0d3 222 }
94148c90 223
1378c0d3
C
224 if (abuse.reporterAccount) {
225 abuse.reporterAccount = new Account(abuse.reporterAccount)
226 }
94148c90 227
1378c0d3
C
228 if (abuse.flaggedAccount) {
229 abuse.flaggedAccount = new Account(abuse.flaggedAccount)
230 }
94148c90 231
1378c0d3 232 if (abuse.updatedAt === abuse.createdAt) delete abuse.updatedAt
94148c90 233
1378c0d3
C
234 this.abuses.push(abuse)
235 }
236 },
94148c90 237
1378c0d3
C
238 error: err => this.notifier.error(err.message)
239 })
94148c90
C
240 }
241
242 private buildInternalActions (): DropdownAction<ProcessedAbuse>[] {
243 return [
244 {
66357162 245 label: $localize`Internal actions`,
94148c90
C
246 isHeader: true
247 },
248 {
249 label: this.isAdminView()
66357162
C
250 ? $localize`Messages with reporter`
251 : $localize`Messages with moderators`,
94148c90
C
252 handler: abuse => this.openAbuseMessagesModal(abuse),
253 isDisplayed: abuse => this.isLocalAbuse(abuse)
254 },
255 {
66357162 256 label: $localize`Update internal note`,
94148c90
C
257 handler: abuse => this.openModerationCommentModal(abuse),
258 isDisplayed: abuse => this.isAdminView() && !!abuse.moderationComment
259 },
260 {
66357162 261 label: $localize`Mark as accepted`,
94148c90
C
262 handler: abuse => this.updateAbuseState(abuse, AbuseState.ACCEPTED),
263 isDisplayed: abuse => this.isAdminView() && !this.isAbuseAccepted(abuse)
264 },
265 {
66357162 266 label: $localize`Mark as rejected`,
94148c90
C
267 handler: abuse => this.updateAbuseState(abuse, AbuseState.REJECTED),
268 isDisplayed: abuse => this.isAdminView() && !this.isAbuseRejected(abuse)
269 },
270 {
66357162 271 label: $localize`Add internal note`,
94148c90
C
272 handler: abuse => this.openModerationCommentModal(abuse),
273 isDisplayed: abuse => this.isAdminView() && !abuse.moderationComment
274 },
275 {
66357162 276 label: $localize`Delete report`,
94148c90
C
277 handler: abuse => this.isAdminView() && this.removeAbuse(abuse)
278 }
279 ]
280 }
281
282 private buildFlaggedAccountActions (): DropdownAction<ProcessedAbuse>[] {
283 if (!this.isAdminView()) return []
284
285 return [
286 {
66357162 287 label: $localize`Actions for the flagged account`,
94148c90
C
288 isHeader: true,
289 isDisplayed: abuse => abuse.flaggedAccount && !abuse.comment && !abuse.video
290 },
291
292 {
66357162 293 label: $localize`Mute account`,
94148c90
C
294 isDisplayed: abuse => abuse.flaggedAccount && !abuse.comment && !abuse.video,
295 handler: abuse => this.muteAccountHelper(abuse.flaggedAccount)
296 },
297
298 {
66357162 299 label: $localize`Mute server account`,
94148c90
C
300 isDisplayed: abuse => abuse.flaggedAccount && !abuse.comment && !abuse.video,
301 handler: abuse => this.muteServerHelper(abuse.flaggedAccount.host)
302 }
303 ]
304 }
305
306 private buildAccountActions (): DropdownAction<ProcessedAbuse>[] {
307 if (!this.isAdminView()) return []
308
309 return [
310 {
66357162 311 label: $localize`Actions for the reporter`,
94148c90
C
312 isHeader: true,
313 isDisplayed: abuse => !!abuse.reporterAccount
314 },
315
316 {
66357162 317 label: $localize`Mute reporter`,
94148c90
C
318 isDisplayed: abuse => !!abuse.reporterAccount,
319 handler: abuse => this.muteAccountHelper(abuse.reporterAccount)
320 },
321
322 {
66357162 323 label: $localize`Mute server`,
94148c90
C
324 isDisplayed: abuse => abuse.reporterAccount && !abuse.reporterAccount.userId,
325 handler: abuse => this.muteServerHelper(abuse.reporterAccount.host)
326 }
327 ]
328 }
329
330 private buildVideoActions (): DropdownAction<ProcessedAbuse>[] {
331 if (!this.isAdminView()) return []
332
333 return [
334 {
66357162 335 label: $localize`Actions for the video`,
94148c90
C
336 isHeader: true,
337 isDisplayed: abuse => abuse.video && !abuse.video.deleted
338 },
339 {
66357162 340 label: $localize`Block video`,
94148c90
C
341 isDisplayed: abuse => abuse.video && !abuse.video.deleted && !abuse.video.blacklisted,
342 handler: abuse => {
3cfa8176 343 this.videoBlocklistService.blockVideo([ { videoId: abuse.video.id, unfederate: abuse.video.channel.isLocal } ])
1378c0d3
C
344 .subscribe({
345 next: () => {
66357162 346 this.notifier.success($localize`Video blocked.`)
94148c90
C
347
348 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
349 },
350
1378c0d3
C
351 error: err => this.notifier.error(err.message)
352 })
94148c90
C
353 }
354 },
355 {
66357162 356 label: $localize`Unblock video`,
94148c90
C
357 isDisplayed: abuse => abuse.video && !abuse.video.deleted && abuse.video.blacklisted,
358 handler: abuse => {
359 this.videoBlocklistService.unblockVideo(abuse.video.id)
1378c0d3
C
360 .subscribe({
361 next: () => {
66357162 362 this.notifier.success($localize`Video unblocked.`)
94148c90
C
363
364 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
365 },
366
1378c0d3
C
367 error: err => this.notifier.error(err.message)
368 })
94148c90
C
369 }
370 },
371 {
66357162 372 label: $localize`Delete video`,
94148c90
C
373 isDisplayed: abuse => abuse.video && !abuse.video.deleted,
374 handler: async abuse => {
375 const res = await this.confirmService.confirm(
66357162
C
376 $localize`Do you really want to delete this video?`,
377 $localize`Delete`
94148c90
C
378 )
379 if (res === false) return
380
381 this.videoService.removeVideo(abuse.video.id)
1378c0d3
C
382 .subscribe({
383 next: () => {
66357162 384 this.notifier.success($localize`Video deleted.`)
94148c90
C
385
386 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
387 },
388
1378c0d3
C
389 error: err => this.notifier.error(err.message)
390 })
94148c90
C
391 }
392 }
393 ]
394 }
395
396 private buildCommentActions (): DropdownAction<ProcessedAbuse>[] {
397 if (!this.isAdminView()) return []
398
399 return [
400 {
66357162 401 label: $localize`Actions for the comment`,
94148c90
C
402 isHeader: true,
403 isDisplayed: abuse => abuse.comment && !abuse.comment.deleted
404 },
405
406 {
66357162 407 label: $localize`Delete comment`,
94148c90
C
408 isDisplayed: abuse => abuse.comment && !abuse.comment.deleted,
409 handler: async abuse => {
410 const res = await this.confirmService.confirm(
66357162
C
411 $localize`Do you really want to delete this comment?`,
412 $localize`Delete`
94148c90
C
413 )
414 if (res === false) return
415
416 this.commentService.deleteVideoComment(abuse.comment.video.id, abuse.comment.id)
1378c0d3
C
417 .subscribe({
418 next: () => {
66357162 419 this.notifier.success($localize`Comment deleted.`)
94148c90
C
420
421 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
422 },
423
1378c0d3
C
424 error: err => this.notifier.error(err.message)
425 })
94148c90
C
426 }
427 }
428 ]
429 }
430
431 private muteAccountHelper (account: Account) {
432 this.blocklistService.blockAccountByInstance(account)
1378c0d3
C
433 .subscribe({
434 next: () => {
66357162 435 this.notifier.success($localize`Account ${account.nameWithHost} muted by the instance.`)
94148c90
C
436 account.mutedByInstance = true
437 },
438
1378c0d3
C
439 error: err => this.notifier.error(err.message)
440 })
94148c90
C
441 }
442
443 private muteServerHelper (host: string) {
444 this.blocklistService.blockServerByInstance(host)
1378c0d3
C
445 .subscribe({
446 next: () => {
66357162 447 this.notifier.success($localize`Server ${host} muted by the instance.`)
94148c90
C
448 },
449
1378c0d3
C
450 error: err => this.notifier.error(err.message)
451 })
94148c90
C
452 }
453
454 private toHtml (text: string) {
455 return this.markdownRenderer.textMarkdownToHTML(text)
456 }
457}