From 5baee5fca418487e72ddcd6419d31bca8659b668 Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Tue, 2 Jun 2020 20:50:42 +0200 Subject: rename blacklist to block/blocklist, merge block and auto-block views - also replace whitelist with allowlist - add advanced filters for video-block-list view - move icons in video-block-list and video-abuse-list to left side for visibility - add robot icon to depict automated nature of a block in video-block-list resolves #2790 --- .../video-block-list/video-block-list.component.ts | 196 +++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts (limited to 'client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts') diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts new file mode 100644 index 000000000..e72ab5348 --- /dev/null +++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts @@ -0,0 +1,196 @@ +import { Component, OnInit } from '@angular/core' +import { SortMeta } from 'primeng/api' +import { Notifier, ServerService } from '@app/core' +import { ConfirmService } from '../../../core' +import { RestPagination, RestTable, VideoBlockService } from '../../../shared' +import { VideoBlocklist, VideoBlockType } from '../../../../../../shared' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' +import { Video } from '../../../shared/video/video.model' +import { MarkdownService } from '@app/shared/renderer' +import { Params, ActivatedRoute, Router } from '@angular/router' +import { filter, switchMap } from 'rxjs/operators' +import { VideoService } from '@app/shared/video/video.service' + +@Component({ + selector: 'my-video-block-list', + templateUrl: './video-block-list.component.html', + styleUrls: [ '../moderation.component.scss', './video-block-list.component.scss' ] +}) +export class VideoBlockListComponent extends RestTable implements OnInit { + blocklist: (VideoBlocklist & { reasonHtml?: string })[] = [] + totalRecords = 0 + sort: SortMeta = { field: 'createdAt', order: -1 } + pagination: RestPagination = { count: this.rowsPerPage, start: 0 } + listBlockTypeFilter: VideoBlockType = undefined + + videoBlocklistActions: DropdownAction[][] = [] + + constructor ( + private notifier: Notifier, + private serverService: ServerService, + private confirmService: ConfirmService, + private videoBlocklistService: VideoBlockService, + private markdownRenderer: MarkdownService, + private videoService: VideoService, + private route: ActivatedRoute, + private router: Router, + private i18n: I18n + ) { + super() + + this.videoBlocklistActions = [ + [ + { + label: this.i18n('Internal actions'), + isHeader: true + }, + { + label: this.i18n('Switch video block to manual'), + handler: videoBlock => { + this.videoBlocklistService.unblockVideo(videoBlock.video.id).pipe( + switchMap(_ => this.videoBlocklistService.blockVideo(videoBlock.video.id, undefined, true)) + ).subscribe( + () => { + this.notifier.success(this.i18n('Video {{name}} switched to manual block.', { name: videoBlock.video.name })) + this.loadData() + }, + + err => this.notifier.error(err.message) + ) + } + } + ], + [ + { + label: this.i18n('Actions for the video'), + isHeader: true, + }, + { + label: this.i18n('Unblock video'), + handler: videoBlock => this.unblockVideo(videoBlock) + }, + + { + label: this.i18n('Delete video'), + handler: async videoBlock => { + const res = await this.confirmService.confirm( + this.i18n('Do you really want to delete this video?'), + this.i18n('Delete') + ) + if (res === false) return + + this.videoService.removeVideo(videoBlock.video.id) + .subscribe( + () => { + this.notifier.success(this.i18n('Video deleted.')) + }, + + err => this.notifier.error(err.message) + ) + } + } + ] + ] + } + + ngOnInit () { + this.serverService.getConfig() + .subscribe(config => { + // don't filter if auto-blacklist is not enabled as this will be the only list + if (config.autoBlacklist.videos.ofUsers.enabled) { + this.listBlockTypeFilter = VideoBlockType.MANUAL + } + }) + + this.initialize() + + this.route.queryParams + .pipe(filter(params => params.search !== undefined && params.search !== null)) + .subscribe(params => { + this.search = params.search + this.setTableFilter(params.search) + this.loadData() + }) + } + + ngAfterViewInit () { + if (this.search) this.setTableFilter(this.search) + } + + /* Table filter functions */ + onBlockSearch (event: Event) { + this.onSearch(event) + this.setQueryParams((event.target as HTMLInputElement).value) + } + + setQueryParams (search: string) { + const queryParams: Params = {} + if (search) Object.assign(queryParams, { search }) + this.router.navigate([ '/admin/moderation/video-blocks/list' ], { queryParams }) + } + + resetTableFilter () { + this.setTableFilter('') + this.setQueryParams('') + this.resetSearch() + } + /* END Table filter functions */ + + getIdentifier () { + return 'VideoBlockListComponent' + } + + getVideoUrl (videoBlock: VideoBlocklist) { + return Video.buildClientUrl(videoBlock.video.uuid) + } + + booleanToText (value: boolean) { + if (value === true) return this.i18n('yes') + + return this.i18n('no') + } + + toHtml (text: string) { + return this.markdownRenderer.textMarkdownToHTML(text) + } + + async unblockVideo (entry: VideoBlocklist) { + const confirmMessage = this.i18n( + 'Do you really want to unblock this video? It will be available again in the videos list.' + ) + + const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblock')) + if (res === false) return + + this.videoBlocklistService.unblockVideo(entry.video.id).subscribe( + () => { + this.notifier.success(this.i18n('Video {{name}} unblocked.', { name: entry.video.name })) + this.loadData() + }, + + err => this.notifier.error(err.message) + ) + } + + protected loadData () { + this.videoBlocklistService.listBlocks({ + pagination: this.pagination, + sort: this.sort, + search: this.search, + }) + .subscribe( + async resultList => { + this.totalRecords = resultList.total + + this.blocklist = resultList.data + + for (const element of this.blocklist) { + Object.assign(element, { reasonHtml: await this.toHtml(element.reason) }) + } + }, + + err => this.notifier.error(err.message) + ) + } +} -- cgit v1.2.3