Merge branch 'blacklist' into 'develop'
[oweals/peertube.git] / client / src / app / shared / video / video-actions-dropdown.component.ts
1 import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core'
2 import { I18n } from '@ngx-translate/i18n-polyfill'
3 import { DropdownAction, DropdownButtonSize, DropdownDirection } from '@app/shared/buttons/action-dropdown.component'
4 import { AuthService, ConfirmService, Notifier } from '@app/core'
5 import { Video } from '@app/shared/video/video.model'
6 import { VideoService } from '@app/shared/video/video.service'
7 import { VideoDetails } from '@app/shared/video/video-details.model'
8 import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
9 import { VideoAddToPlaylistComponent } from '@app/shared/video-playlist/video-add-to-playlist.component'
10 import { VideoDownloadComponent } from '@app/shared/video/modals/video-download.component'
11 import { VideoReportComponent } from '@app/shared/video/modals/video-report.component'
12 import { VideoBlockComponent } from '@app/shared/video/modals/video-block.component'
13 import { VideoBlockService } from '@app/shared/video-block'
14 import { ScreenService } from '@app/shared/misc/screen.service'
15 import { VideoCaption } from '@shared/models'
16 import { RedundancyService } from '@app/shared/video/redundancy.service'
17
18 export type VideoActionsDisplayType = {
19   playlist?: boolean
20   download?: boolean
21   update?: boolean
22   blacklist?: boolean
23   delete?: boolean
24   report?: boolean
25   duplicate?: boolean
26 }
27
28 @Component({
29   selector: 'my-video-actions-dropdown',
30   templateUrl: './video-actions-dropdown.component.html',
31   styleUrls: [ './video-actions-dropdown.component.scss' ]
32 })
33 export class VideoActionsDropdownComponent implements OnChanges {
34   @ViewChild('playlistDropdown') playlistDropdown: NgbDropdown
35   @ViewChild('playlistAdd') playlistAdd: VideoAddToPlaylistComponent
36
37   @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent
38   @ViewChild('videoReportModal') videoReportModal: VideoReportComponent
39   @ViewChild('videoBlockModal') videoBlockModal: VideoBlockComponent
40
41   @Input() video: Video | VideoDetails
42   @Input() videoCaptions: VideoCaption[] = []
43
44   @Input() displayOptions: VideoActionsDisplayType = {
45     playlist: false,
46     download: true,
47     update: true,
48     blacklist: true,
49     delete: true,
50     report: true,
51     duplicate: true
52   }
53   @Input() placement = 'left'
54
55   @Input() label: string
56
57   @Input() buttonStyled = false
58   @Input() buttonSize: DropdownButtonSize = 'normal'
59   @Input() buttonDirection: DropdownDirection = 'vertical'
60
61   @Output() videoRemoved = new EventEmitter()
62   @Output() videoUnblocked = new EventEmitter()
63   @Output() videoBlocked = new EventEmitter()
64   @Output() modalOpened = new EventEmitter()
65
66   videoActions: DropdownAction<{ video: Video }>[][] = []
67
68   private loaded = false
69
70   constructor (
71     private authService: AuthService,
72     private notifier: Notifier,
73     private confirmService: ConfirmService,
74     private videoBlocklistService: VideoBlockService,
75     private screenService: ScreenService,
76     private videoService: VideoService,
77     private redundancyService: RedundancyService,
78     private i18n: I18n
79   ) { }
80
81   get user () {
82     return this.authService.getUser()
83   }
84
85   ngOnChanges () {
86     if (this.loaded) {
87       this.loaded = false
88       this.playlistAdd.reload()
89     }
90
91     this.buildActions()
92   }
93
94   isUserLoggedIn () {
95     return this.authService.isLoggedIn()
96   }
97
98   loadDropdownInformation () {
99     if (!this.isUserLoggedIn() || this.loaded === true) return
100
101     this.loaded = true
102
103     if (this.displayOptions.playlist) this.playlistAdd.load()
104   }
105
106   /* Show modals */
107
108   showDownloadModal () {
109     this.modalOpened.emit()
110
111     this.videoDownloadModal.show(this.video as VideoDetails, this.videoCaptions)
112   }
113
114   showReportModal () {
115     this.modalOpened.emit()
116
117     this.videoReportModal.show()
118   }
119
120   showBlockModal () {
121     this.modalOpened.emit()
122
123     this.videoBlockModal.show()
124   }
125
126   /* Actions checker */
127
128   isVideoUpdatable () {
129     return this.video.isUpdatableBy(this.user)
130   }
131
132   isVideoRemovable () {
133     return this.video.isRemovableBy(this.user)
134   }
135
136   isVideoBlockable () {
137     return this.video.isBlockableBy(this.user)
138   }
139
140   isVideoUnblockable () {
141     return this.video.isUnblockableBy(this.user)
142   }
143
144   isVideoDownloadable () {
145     return this.video && this.video instanceof VideoDetails && this.video.downloadEnabled
146   }
147
148   canVideoBeDuplicated () {
149     return this.video.canBeDuplicatedBy(this.user)
150   }
151
152   /* Action handlers */
153
154   async unblockVideo () {
155     const confirmMessage = this.i18n(
156       'Do you really want to unblock this video? It will be available again in the videos list.'
157     )
158
159     const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblock'))
160     if (res === false) return
161
162     this.videoBlocklistService.unblockVideo(this.video.id).subscribe(
163       () => {
164         this.notifier.success(this.i18n('Video {{name}} unblocked.', { name: this.video.name }))
165
166         this.video.blacklisted = false
167         this.video.blockedReason = null
168
169         this.videoUnblocked.emit()
170       },
171
172       err => this.notifier.error(err.message)
173     )
174   }
175
176   async removeVideo () {
177     this.modalOpened.emit()
178
179     const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this video?'), this.i18n('Delete'))
180     if (res === false) return
181
182     this.videoService.removeVideo(this.video.id)
183         .subscribe(
184           () => {
185             this.notifier.success(this.i18n('Video {{videoName}} deleted.', { videoName: this.video.name }))
186
187             this.videoRemoved.emit()
188           },
189
190           error => this.notifier.error(error.message)
191         )
192   }
193
194   duplicateVideo () {
195     this.redundancyService.addVideoRedundancy(this.video)
196       .subscribe(
197         () => {
198           const message = this.i18n('This video will be duplicated by your instance.')
199           this.notifier.success(message)
200         },
201
202         err => this.notifier.error(err.message)
203       )
204   }
205
206   onVideoBlocked () {
207     this.videoBlocked.emit()
208   }
209
210   getPlaylistDropdownPlacement () {
211     if (this.screenService.isInSmallView()) {
212       return 'bottom-right'
213     }
214
215     return 'bottom-left bottom-right'
216   }
217
218   private buildActions () {
219     this.videoActions = [
220       [
221         {
222           label: this.i18n('Save to playlist'),
223           handler: () => this.playlistDropdown.toggle(),
224           isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.playlist,
225           iconName: 'playlist-add'
226         }
227       ],
228       [
229         {
230           label: this.i18n('Download'),
231           handler: () => this.showDownloadModal(),
232           isDisplayed: () => this.displayOptions.download && this.isVideoDownloadable(),
233           iconName: 'download'
234         },
235         {
236           label: this.i18n('Update'),
237           linkBuilder: ({ video }) => [ '/videos/update', video.uuid ],
238           iconName: 'edit',
239           isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.update && this.isVideoUpdatable()
240         },
241         {
242           label: this.i18n('Block'),
243           handler: () => this.showBlockModal(),
244           iconName: 'no',
245           isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.blacklist && this.isVideoBlockable()
246         },
247         {
248           label: this.i18n('Unblock'),
249           handler: () => this.unblockVideo(),
250           iconName: 'undo',
251           isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.blacklist && this.isVideoUnblockable()
252         },
253         {
254           label: this.i18n('Mirror'),
255           handler: () => this.duplicateVideo(),
256           isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.duplicate && this.canVideoBeDuplicated(),
257           iconName: 'cloud-download'
258         },
259         {
260           label: this.i18n('Delete'),
261           handler: () => this.removeVideo(),
262           isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.delete && this.isVideoRemovable(),
263           iconName: 'delete'
264         }
265       ],
266       [
267         {
268           label: this.i18n('Report'),
269           handler: () => this.showReportModal(),
270           isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.report,
271           iconName: 'alert'
272         }
273       ]
274     ]
275   }
276 }