]>
Commit | Line | Data |
---|---|---|
36004aa7 | 1 | import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core' |
bb152476 | 2 | import { Account } from '@app/shared/account/account.model' |
f8b2c1b4 | 3 | import { Notifier } from '@app/core' |
f77eb73b | 4 | import { SortMeta } from 'primeng/api' |
efc9e845 | 5 | import { VideoAbuse, VideoAbuseState } from '../../../../../../shared' |
bb152476 | 6 | import { RestPagination, RestTable, VideoAbuseService, VideoBlacklistService } from '../../../shared' |
b1d40cff | 7 | import { I18n } from '@ngx-translate/i18n-polyfill' |
614d1ae9 C |
8 | import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' |
9 | import { ConfirmService } from '../../../core/index' | |
efc9e845 | 10 | import { ModerationCommentModalComponent } from './moderation-comment-modal.component' |
614d1ae9 | 11 | import { Video } from '../../../shared/video/video.model' |
1506307f | 12 | import { MarkdownService } from '@app/shared/renderer' |
d6af8146 RK |
13 | import { Actor } from '@app/shared/actor/actor.model' |
14 | import { buildVideoLink, buildVideoEmbed } from 'src/assets/player/utils' | |
15 | import { getAbsoluteAPIUrl } from '@app/shared/misc/utils' | |
16 | import { DomSanitizer } from '@angular/platform-browser' | |
bb152476 | 17 | import { BlocklistService } from '@app/shared/blocklist' |
9b4241e3 | 18 | import { VideoService } from '@app/shared/video/video.service' |
844db39e | 19 | import { ActivatedRoute } from '@angular/router' |
36004aa7 | 20 | import { filter } from 'rxjs/operators' |
19a3b914 | 21 | |
11ac88de | 22 | @Component({ |
df98563e | 23 | selector: 'my-video-abuse-list', |
f595d394 | 24 | templateUrl: './video-abuse-list.component.html', |
86521a67 | 25 | styleUrls: [ '../moderation.component.scss', './video-abuse-list.component.scss' ] |
11ac88de | 26 | }) |
36004aa7 | 27 | export class VideoAbuseListComponent extends RestTable implements OnInit, AfterViewInit { |
f36da21e | 28 | @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent |
efc9e845 | 29 | |
41d71344 | 30 | videoAbuses: (VideoAbuse & { moderationCommentHtml?: string, reasonHtml?: string })[] = [] |
d592e0a9 | 31 | totalRecords = 0 |
ab998f7b | 32 | sort: SortMeta = { field: 'createdAt', order: 1 } |
d592e0a9 | 33 | pagination: RestPagination = { count: this.rowsPerPage, start: 0 } |
11ac88de | 34 | |
bb152476 | 35 | videoAbuseActions: DropdownAction<VideoAbuse>[][] = [] |
efc9e845 | 36 | |
df98563e | 37 | constructor ( |
f8b2c1b4 | 38 | private notifier: Notifier, |
b1d40cff | 39 | private videoAbuseService: VideoAbuseService, |
bb152476 | 40 | private blocklistService: BlocklistService, |
9b4241e3 | 41 | private videoService: VideoService, |
bb152476 | 42 | private videoBlacklistService: VideoBlacklistService, |
efc9e845 | 43 | private confirmService: ConfirmService, |
1506307f | 44 | private i18n: I18n, |
d6af8146 | 45 | private markdownRenderer: MarkdownService, |
844db39e | 46 | private sanitizer: DomSanitizer, |
5fd4ca00 | 47 | private route: ActivatedRoute |
28798b5d | 48 | ) { |
d592e0a9 | 49 | super() |
efc9e845 C |
50 | |
51 | this.videoAbuseActions = [ | |
bb152476 RK |
52 | [ |
53 | { | |
54 | label: this.i18n('Internal actions'), | |
55 | isHeader: true | |
56 | }, | |
57 | { | |
58 | label: this.i18n('Delete report'), | |
59 | handler: videoAbuse => this.removeVideoAbuse(videoAbuse) | |
60 | }, | |
61 | { | |
62 | label: this.i18n('Add note'), | |
63 | handler: videoAbuse => this.openModerationCommentModal(videoAbuse), | |
64 | isDisplayed: videoAbuse => !videoAbuse.moderationComment | |
65 | }, | |
66 | { | |
67 | label: this.i18n('Update note'), | |
68 | handler: videoAbuse => this.openModerationCommentModal(videoAbuse), | |
69 | isDisplayed: videoAbuse => !!videoAbuse.moderationComment | |
70 | }, | |
71 | { | |
72 | label: this.i18n('Mark as accepted'), | |
73 | handler: videoAbuse => this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED), | |
74 | isDisplayed: videoAbuse => !this.isVideoAbuseAccepted(videoAbuse) | |
75 | }, | |
76 | { | |
77 | label: this.i18n('Mark as rejected'), | |
78 | handler: videoAbuse => this.updateVideoAbuseState(videoAbuse, VideoAbuseState.REJECTED), | |
79 | isDisplayed: videoAbuse => !this.isVideoAbuseRejected(videoAbuse) | |
80 | } | |
81 | ], | |
82 | [ | |
83 | { | |
84 | label: this.i18n('Actions for the video'), | |
9b4241e3 RK |
85 | isHeader: true, |
86 | isDisplayed: videoAbuse => !videoAbuse.video.deleted | |
bb152476 RK |
87 | }, |
88 | { | |
89 | label: this.i18n('Blacklist video'), | |
86521a67 | 90 | isDisplayed: videoAbuse => !videoAbuse.video.deleted && !videoAbuse.video.blacklisted, |
bb152476 RK |
91 | handler: videoAbuse => { |
92 | this.videoBlacklistService.blacklistVideo(videoAbuse.video.id, undefined, true) | |
93 | .subscribe( | |
94 | () => { | |
95 | this.notifier.success(this.i18n('Video blacklisted.')) | |
96 | ||
97 | this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED) | |
98 | }, | |
99 | ||
9b4241e3 RK |
100 | err => this.notifier.error(err.message) |
101 | ) | |
102 | } | |
103 | }, | |
86521a67 RK |
104 | { |
105 | label: this.i18n('Unblacklist video'), | |
106 | isDisplayed: videoAbuse => !videoAbuse.video.deleted && videoAbuse.video.blacklisted, | |
107 | handler: videoAbuse => { | |
108 | this.videoBlacklistService.removeVideoFromBlacklist(videoAbuse.video.id) | |
109 | .subscribe( | |
110 | () => { | |
111 | this.notifier.success(this.i18n('Video unblacklisted.')) | |
112 | ||
113 | this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED) | |
114 | }, | |
115 | ||
116 | err => this.notifier.error(err.message) | |
117 | ) | |
118 | } | |
119 | }, | |
9b4241e3 RK |
120 | { |
121 | label: this.i18n('Delete video'), | |
122 | isDisplayed: videoAbuse => !videoAbuse.video.deleted, | |
123 | handler: async videoAbuse => { | |
86521a67 RK |
124 | const res = await this.confirmService.confirm( |
125 | this.i18n('Do you really want to delete this video?'), | |
126 | this.i18n('Delete') | |
127 | ) | |
9b4241e3 RK |
128 | if (res === false) return |
129 | ||
130 | this.videoService.removeVideo(videoAbuse.video.id) | |
131 | .subscribe( | |
132 | () => { | |
133 | this.notifier.success(this.i18n('Video deleted.')) | |
134 | ||
135 | this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED) | |
136 | }, | |
137 | ||
138 | err => this.notifier.error(err.message) | |
139 | ) | |
140 | } | |
141 | } | |
142 | ], | |
143 | [ | |
144 | { | |
145 | label: this.i18n('Actions for the reporter'), | |
146 | isHeader: true | |
147 | }, | |
148 | { | |
86521a67 | 149 | label: this.i18n('Mute reporter'), |
9b4241e3 RK |
150 | handler: async videoAbuse => { |
151 | const account = videoAbuse.reporterAccount as Account | |
152 | ||
153 | this.blocklistService.blockAccountByInstance(account) | |
154 | .subscribe( | |
155 | () => { | |
86521a67 RK |
156 | this.notifier.success( |
157 | this.i18n('Account {{nameWithHost}} muted by the instance.', { nameWithHost: account.nameWithHost }) | |
158 | ) | |
9b4241e3 RK |
159 | |
160 | account.mutedByInstance = true | |
161 | }, | |
162 | ||
86521a67 RK |
163 | err => this.notifier.error(err.message) |
164 | ) | |
165 | } | |
166 | }, | |
167 | { | |
168 | label: this.i18n('Mute server'), | |
169 | isDisplayed: videoAbuse => !videoAbuse.reporterAccount.userId, | |
170 | handler: async videoAbuse => { | |
171 | this.blocklistService.blockServerByInstance(videoAbuse.reporterAccount.host) | |
172 | .subscribe( | |
173 | () => { | |
174 | this.notifier.success( | |
175 | this.i18n('Server {{host}} muted by the instance.', { host: videoAbuse.reporterAccount.host }) | |
176 | ) | |
177 | }, | |
178 | ||
bb152476 RK |
179 | err => this.notifier.error(err.message) |
180 | ) | |
181 | } | |
182 | } | |
183 | ] | |
efc9e845 | 184 | ] |
d592e0a9 C |
185 | } |
186 | ||
187 | ngOnInit () { | |
24b9417c | 188 | this.initialize() |
844db39e RK |
189 | |
190 | this.route.queryParams | |
36004aa7 RK |
191 | .pipe(filter(params => params.search !== undefined && params.search !== null)) |
192 | .subscribe(params => { | |
193 | this.search = params.search | |
194 | this.setTableFilter(params.search) | |
195 | this.loadData() | |
196 | }) | |
197 | } | |
198 | ||
199 | ngAfterViewInit () { | |
0251197e | 200 | if (this.search) this.setTableFilter(this.search) |
df98563e | 201 | } |
11ac88de | 202 | |
8e11a1b3 C |
203 | getIdentifier () { |
204 | return 'VideoAbuseListComponent' | |
205 | } | |
206 | ||
efc9e845 C |
207 | openModerationCommentModal (videoAbuse: VideoAbuse) { |
208 | this.moderationCommentModal.openModal(videoAbuse) | |
209 | } | |
210 | ||
211 | onModerationCommentUpdated () { | |
212 | this.loadData() | |
213 | } | |
214 | ||
19a3b914 C |
215 | createByString (account: Account) { |
216 | return Account.CREATE_BY_STRING(account.name, account.host) | |
d592e0a9 C |
217 | } |
218 | ||
36004aa7 | 219 | setTableFilter (filter: string) { |
d1261d9a | 220 | // FIXME: cannot use ViewChild, so create a component for the filter input |
36004aa7 RK |
221 | const filterInput = document.getElementById('table-filter') as HTMLInputElement |
222 | if (filterInput) filterInput.value = filter | |
223 | } | |
224 | ||
efc9e845 C |
225 | isVideoAbuseAccepted (videoAbuse: VideoAbuse) { |
226 | return videoAbuse.state.id === VideoAbuseState.ACCEPTED | |
227 | } | |
228 | ||
229 | isVideoAbuseRejected (videoAbuse: VideoAbuse) { | |
230 | return videoAbuse.state.id === VideoAbuseState.REJECTED | |
231 | } | |
232 | ||
191764f3 C |
233 | getVideoUrl (videoAbuse: VideoAbuse) { |
234 | return Video.buildClientUrl(videoAbuse.video.uuid) | |
235 | } | |
236 | ||
d6af8146 | 237 | getVideoEmbed (videoAbuse: VideoAbuse) { |
042daa70 | 238 | const absoluteAPIUrl = getAbsoluteAPIUrl() |
d6af8146 RK |
239 | const embedUrl = buildVideoLink({ |
240 | baseUrl: absoluteAPIUrl + '/videos/embed/' + videoAbuse.video.uuid, | |
241 | warningTitle: false | |
242 | }) | |
243 | return buildVideoEmbed(embedUrl) | |
244 | } | |
245 | ||
246 | switchToDefaultAvatar ($event: Event) { | |
247 | ($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL() | |
248 | } | |
249 | ||
efc9e845 | 250 | async removeVideoAbuse (videoAbuse: VideoAbuse) { |
198d764f | 251 | const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse report?'), this.i18n('Delete')) |
efc9e845 C |
252 | if (res === false) return |
253 | ||
254 | this.videoAbuseService.removeVideoAbuse(videoAbuse).subscribe( | |
255 | () => { | |
f8b2c1b4 | 256 | this.notifier.success(this.i18n('Abuse deleted.')) |
efc9e845 C |
257 | this.loadData() |
258 | }, | |
259 | ||
f8b2c1b4 | 260 | err => this.notifier.error(err.message) |
efc9e845 C |
261 | ) |
262 | } | |
263 | ||
264 | updateVideoAbuseState (videoAbuse: VideoAbuse, state: VideoAbuseState) { | |
265 | this.videoAbuseService.updateVideoAbuse(videoAbuse, { state }) | |
266 | .subscribe( | |
267 | () => this.loadData(), | |
268 | ||
f8b2c1b4 | 269 | err => this.notifier.error(err.message) |
efc9e845 C |
270 | ) |
271 | ||
272 | } | |
273 | ||
d592e0a9 | 274 | protected loadData () { |
844db39e RK |
275 | return this.videoAbuseService.getVideoAbuses({ |
276 | pagination: this.pagination, | |
042daa70 | 277 | sort: this.sort, |
844db39e RK |
278 | search: this.search |
279 | }).subscribe( | |
280 | async resultList => { | |
281 | this.totalRecords = resultList.total | |
282 | ||
283 | this.videoAbuses = resultList.data | |
284 | ||
285 | for (const abuse of this.videoAbuses) { | |
286 | Object.assign(abuse, { | |
287 | reasonHtml: await this.toHtml(abuse.reason), | |
288 | moderationCommentHtml: await this.toHtml(abuse.moderationComment), | |
289 | embedHtml: this.sanitizer.bypassSecurityTrustHtml(this.getVideoEmbed(abuse)), | |
290 | reporterAccount: new Account(abuse.reporterAccount) | |
291 | }) | |
0db536f1 | 292 | if (abuse.updatedAt === abuse.createdAt) delete abuse.updatedAt |
844db39e RK |
293 | } |
294 | ||
295 | }, | |
296 | ||
297 | err => this.notifier.error(err.message) | |
298 | ) | |
11ac88de | 299 | } |
41d71344 C |
300 | |
301 | private toHtml (text: string) { | |
302 | return this.markdownRenderer.textMarkdownToHTML(text) | |
303 | } | |
11ac88de | 304 | } |