aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+admin/moderation
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-02-28 16:27:25 +0100
committerChocobozzz <me@florianbigard.com>2022-02-28 16:27:25 +0100
commit5a51ecc2172282786dab47bd874026621554ba6d (patch)
tree129a6bc40d2f048fd73362e427b75974f040ac5f /client/src/app/+admin/moderation
parentf1c70a8666e53414f4e604290d35d26ae725b691 (diff)
downloadPeerTube-5a51ecc2172282786dab47bd874026621554ba6d.tar.gz
PeerTube-5a51ecc2172282786dab47bd874026621554ba6d.tar.zst
PeerTube-5a51ecc2172282786dab47bd874026621554ba6d.zip
Move admin comments list in overviews menu
Diffstat (limited to 'client/src/app/+admin/moderation')
-rw-r--r--client/src/app/+admin/moderation/moderation.routes.ts12
-rw-r--r--client/src/app/+admin/moderation/video-comment-list/index.ts1
-rw-r--r--client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html111
-rw-r--r--client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss52
-rw-r--r--client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts186
5 files changed, 3 insertions, 359 deletions
diff --git a/client/src/app/+admin/moderation/moderation.routes.ts b/client/src/app/+admin/moderation/moderation.routes.ts
index 5c39ff366..1ad301039 100644
--- a/client/src/app/+admin/moderation/moderation.routes.ts
+++ b/client/src/app/+admin/moderation/moderation.routes.ts
@@ -2,7 +2,6 @@ import { Routes } from '@angular/router'
2import { AbuseListComponent } from '@app/+admin/moderation/abuse-list' 2import { AbuseListComponent } from '@app/+admin/moderation/abuse-list'
3import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist' 3import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist'
4import { VideoBlockListComponent } from '@app/+admin/moderation/video-block-list' 4import { VideoBlockListComponent } from '@app/+admin/moderation/video-block-list'
5import { VideoCommentListComponent } from './video-comment-list'
6import { UserRightGuard } from '@app/core' 5import { UserRightGuard } from '@app/core'
7import { UserRight } from '@shared/models' 6import { UserRight } from '@shared/models'
8 7
@@ -69,6 +68,7 @@ export const ModerationRoutes: Routes = [
69 } 68 }
70 }, 69 },
71 70
71 // We move this component in admin overview pages
72 { 72 {
73 path: 'video-comments', 73 path: 'video-comments',
74 redirectTo: 'video-comments/list', 74 redirectTo: 'video-comments/list',
@@ -76,14 +76,8 @@ export const ModerationRoutes: Routes = [
76 }, 76 },
77 { 77 {
78 path: 'video-comments/list', 78 path: 'video-comments/list',
79 component: VideoCommentListComponent, 79 redirectTo: '/admin/comments/list',
80 canActivate: [ UserRightGuard ], 80 pathMatch: 'full'
81 data: {
82 userRight: UserRight.SEE_ALL_COMMENTS,
83 meta: {
84 title: $localize`Video comments`
85 }
86 }
87 }, 81 },
88 82
89 { 83 {
diff --git a/client/src/app/+admin/moderation/video-comment-list/index.ts b/client/src/app/+admin/moderation/video-comment-list/index.ts
deleted file mode 100644
index eb08b4177..000000000
--- a/client/src/app/+admin/moderation/video-comment-list/index.ts
+++ /dev/null
@@ -1 +0,0 @@
1export * from './video-comment-list.component'
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html
deleted file mode 100644
index 0dbbbe1cc..000000000
--- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.html
+++ /dev/null
@@ -1,111 +0,0 @@
1<h1>
2 <my-global-icon iconName="message-circle" aria-hidden="true"></my-global-icon>
3 <ng-container i18n>Video comments</ng-container>
4
5 <my-feed [syndicationItems]="syndicationItems"></my-feed>
6</h1>
7
8<em i18n>This view also shows comments from muted accounts.</em>
9
10<p-table
11 [value]="comments" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
12 [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
13 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [selectionPageOnly]="true"
14 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
15 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} comments"
16 [expandedRowKeys]="expandedRows" [(selection)]="selectedComments"
17>
18 <ng-template pTemplate="caption">
19 <div class="caption">
20 <div>
21 <my-action-dropdown
22 *ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
23 [actions]="bulkCommentActions" [entry]="selectedComments"
24 >
25 </my-action-dropdown>
26 </div>
27
28 <div class="ml-auto">
29 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
30 </div>
31 </div>
32 </ng-template>
33
34 <ng-template pTemplate="header">
35 <tr>
36 <th style="width: 40px;">
37 <p-tableHeaderCheckbox ariaLabel="Select all rows" i18n-ariaLabel></p-tableHeaderCheckbox>
38 </th>
39 <th style="width: 40px;"></th>
40 <th style="width: 150px;"></th>
41 <th style="width: 300px;" i18n>Account</th>
42 <th style="width: 300px;" i18n>Video</th>
43 <th i18n>Comment</th>
44 <th style="width: 150px;" i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
45 </tr>
46 </ng-template>
47
48 <ng-template pTemplate="body" let-videoComment let-expanded="expanded">
49 <tr [pSelectableRow]="videoComment">
50
51 <td class="checkbox-cell">
52 <p-tableCheckbox [value]="videoComment" ariaLabel="Select this row" i18n-ariaLabel></p-tableCheckbox>
53 </td>
54
55 <td class="expand-cell" [pRowToggler]="videoComment">
56 <my-table-expander-icon i18n-ngbTooltip ngbTooltip="See full comment" [expanded]="expanded"></my-table-expander-icon>
57 </td>
58
59 <td class="action-cell">
60 <my-action-dropdown
61 [ngClass]="{ 'show': expanded }" placement="bottom-right" container="body"
62 i18n-label label="Actions" [actions]="videoCommentActions" [entry]="videoComment"
63 ></my-action-dropdown>
64 </td>
65
66 <td>
67 <a [href]="videoComment.account.localUrl" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
68 <div class="chip two-lines">
69 <my-actor-avatar [account]="videoComment.account" size="32"></my-actor-avatar>
70 <div>
71 {{ videoComment.account.displayName }}
72 <span>{{ videoComment.by }}</span>
73 </div>
74 </div>
75 </a>
76 </td>
77
78 <td class="video">
79 <em i18n>Commented video</em>
80
81 <a [href]="videoComment.localUrl" target="_blank" rel="noopener noreferrer">{{ videoComment.video.name }}</a>
82 </td>
83
84 <td class="comment-html c-hand" [pRowToggler]="videoComment">
85 <div [innerHTML]="videoComment.textHtml"></div>
86 </td>
87
88 <td class="c-hand" [pRowToggler]="videoComment">{{ videoComment.createdAt | date: 'short' }}</td>
89 </tr>
90 </ng-template>
91
92 <ng-template pTemplate="rowexpansion" let-videoComment>
93 <tr>
94 <td class="expand-cell" colspan="5">
95 <div [innerHTML]="videoComment.textHtml"></div>
96 </td>
97 </tr>
98 </ng-template>
99
100 <ng-template pTemplate="emptymessage">
101 <tr>
102 <td colspan="7">
103 <div class="no-results">
104 <ng-container *ngIf="search" i18n>No comments found matching current filters.</ng-container>
105 <ng-container *ngIf="!search" i18n>No comments found.</ng-container>
106 </div>
107 </td>
108 </tr>
109 </ng-template>
110</p-table>
111
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss
deleted file mode 100644
index 3cf7b8db6..000000000
--- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.scss
+++ /dev/null
@@ -1,52 +0,0 @@
1@use '_mixins' as *;
2@use '_variables' as *;
3
4my-feed {
5 @include margin-left(5px);
6
7 display: inline-block;
8 width: 15px;
9}
10
11my-global-icon {
12 width: 24px;
13 height: 24px;
14}
15
16.video {
17 display: flex;
18 flex-direction: column;
19
20 em {
21 font-size: 11px;
22 }
23
24 a {
25 @include ellipsis;
26
27 color: pvar(--mainForegroundColor);
28 }
29}
30
31.comment-html {
32 ::ng-deep {
33 > div {
34 max-height: 22px;
35 }
36
37 div,
38 p {
39 @include ellipsis;
40 }
41
42 p {
43 margin: 0;
44 }
45 }
46}
47
48@media screen and (max-width: $primeng-breakpoint) {
49 .video {
50 align-items: flex-start !important;
51 }
52}
diff --git a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts b/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
deleted file mode 100644
index 25fe65133..000000000
--- a/client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
+++ /dev/null
@@ -1,186 +0,0 @@
1import { SortMeta } from 'primeng/api'
2import { Component, OnInit } from '@angular/core'
3import { ActivatedRoute, Router } from '@angular/router'
4import { AuthService, ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core'
5import { AdvancedInputFilter } from '@app/shared/shared-forms'
6import { DropdownAction } from '@app/shared/shared-main'
7import { BulkService } from '@app/shared/shared-moderation'
8import { VideoCommentAdmin, VideoCommentService } from '@app/shared/shared-video-comment'
9import { FeedFormat, UserRight } from '@shared/models'
10
11@Component({
12 selector: 'my-video-comment-list',
13 templateUrl: './video-comment-list.component.html',
14 styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-comment-list.component.scss' ]
15})
16export class VideoCommentListComponent extends RestTable implements OnInit {
17 comments: VideoCommentAdmin[]
18 totalRecords = 0
19 sort: SortMeta = { field: 'createdAt', order: -1 }
20 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
21
22 videoCommentActions: DropdownAction<VideoCommentAdmin>[][] = []
23
24 syndicationItems = [
25 {
26 format: FeedFormat.RSS,
27 label: 'media rss 2.0',
28 url: VideoCommentService.BASE_FEEDS_URL + FeedFormat.RSS.toLowerCase()
29 },
30 {
31 format: FeedFormat.ATOM,
32 label: 'atom 1.0',
33 url: VideoCommentService.BASE_FEEDS_URL + FeedFormat.ATOM.toLowerCase()
34 },
35 {
36 format: FeedFormat.JSON,
37 label: 'json 1.0',
38 url: VideoCommentService.BASE_FEEDS_URL + FeedFormat.JSON.toLowerCase()
39 }
40 ]
41
42 selectedComments: VideoCommentAdmin[] = []
43 bulkCommentActions: DropdownAction<VideoCommentAdmin[]>[] = []
44
45 inputFilters: AdvancedInputFilter[] = [
46 {
47 title: $localize`Advanced filters`,
48 children: [
49 {
50 value: 'local:true',
51 label: $localize`Local comments`
52 },
53 {
54 value: 'local:false',
55 label: $localize`Remote comments`
56 }
57 ]
58 }
59 ]
60
61 get authUser () {
62 return this.auth.getUser()
63 }
64
65 constructor (
66 protected router: Router,
67 protected route: ActivatedRoute,
68 private auth: AuthService,
69 private notifier: Notifier,
70 private confirmService: ConfirmService,
71 private videoCommentService: VideoCommentService,
72 private markdownRenderer: MarkdownService,
73 private bulkService: BulkService
74 ) {
75 super()
76
77 this.videoCommentActions = [
78 [
79 {
80 label: $localize`Delete this comment`,
81 handler: comment => this.deleteComment(comment),
82 isDisplayed: () => this.authUser.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)
83 },
84
85 {
86 label: $localize`Delete all comments of this account`,
87 description: $localize`Comments are deleted after a few minutes`,
88 handler: comment => this.deleteUserComments(comment),
89 isDisplayed: () => this.authUser.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)
90 }
91 ]
92 ]
93 }
94
95 ngOnInit () {
96 this.initialize()
97
98 this.bulkCommentActions = [
99 {
100 label: $localize`Delete`,
101 handler: comments => this.removeComments(comments),
102 isDisplayed: () => this.authUser.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT),
103 iconName: 'delete'
104 }
105 ]
106 }
107
108 getIdentifier () {
109 return 'VideoCommentListComponent'
110 }
111
112 toHtml (text: string) {
113 return this.markdownRenderer.textMarkdownToHTML(text, true, true)
114 }
115
116 isInSelectionMode () {
117 return this.selectedComments.length !== 0
118 }
119
120 protected reloadData () {
121 this.videoCommentService.getAdminVideoComments({
122 pagination: this.pagination,
123 sort: this.sort,
124 search: this.search
125 }).subscribe({
126 next: async resultList => {
127 this.totalRecords = resultList.total
128
129 this.comments = []
130
131 for (const c of resultList.data) {
132 this.comments.push(
133 new VideoCommentAdmin(c, await this.toHtml(c.text))
134 )
135 }
136 },
137
138 error: err => this.notifier.error(err.message)
139 })
140 }
141
142 private removeComments (comments: VideoCommentAdmin[]) {
143 const commentArgs = comments.map(c => ({ videoId: c.video.id, commentId: c.id }))
144
145 this.videoCommentService.deleteVideoComments(commentArgs)
146 .subscribe({
147 next: () => {
148 this.notifier.success($localize`${commentArgs.length} comments deleted.`)
149 this.reloadData()
150 },
151
152 error: err => this.notifier.error(err.message),
153
154 complete: () => this.selectedComments = []
155 })
156 }
157
158 private deleteComment (comment: VideoCommentAdmin) {
159 this.videoCommentService.deleteVideoComment(comment.video.id, comment.id)
160 .subscribe({
161 next: () => this.reloadData(),
162
163 error: err => this.notifier.error(err.message)
164 })
165 }
166
167 private async deleteUserComments (comment: VideoCommentAdmin) {
168 const message = $localize`Do you really want to delete all comments of ${comment.by}?`
169 const res = await this.confirmService.confirm(message, $localize`Delete`)
170 if (res === false) return
171
172 const options = {
173 accountName: comment.by,
174 scope: 'instance' as 'instance'
175 }
176
177 this.bulkService.removeCommentsOf(options)
178 .subscribe({
179 next: () => {
180 this.notifier.success($localize`Comments of ${options.accountName} will be deleted in a few minutes`)
181 },
182
183 error: err => this.notifier.error(err.message)
184 })
185 }
186}