diff options
Diffstat (limited to 'client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts')
-rw-r--r-- | client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts | 223 |
1 files changed, 170 insertions, 53 deletions
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts index 41f16e0bf..b6a3408c7 100644 --- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts +++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts | |||
@@ -4,21 +4,27 @@ import { debounceTime, filter } from 'rxjs/operators' | |||
4 | import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core' | 4 | import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core' |
5 | import { AuthService, DisableForReuseHook, Notifier } from '@app/core' | 5 | import { AuthService, DisableForReuseHook, Notifier } from '@app/core' |
6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' |
7 | import { Video, VideoExistInPlaylist, VideoPlaylistCreate, VideoPlaylistElementCreate, VideoPlaylistPrivacy } from '@shared/models' | 7 | import { Video, VideoExistInPlaylist, VideoPlaylistCreate, VideoPlaylistElementCreate, VideoPlaylistPrivacy, VideoPlaylistElementUpdate } from '@shared/models' |
8 | import { secondsToTime } from '../../../assets/player/utils' | 8 | import { secondsToTime } from '../../../assets/player/utils' |
9 | import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators' | 9 | import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators' |
10 | import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service' | 10 | import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service' |
11 | import { invoke, last } from 'lodash' | ||
11 | 12 | ||
12 | const logger = debug('peertube:playlists:VideoAddToPlaylistComponent') | 13 | const logger = debug('peertube:playlists:VideoAddToPlaylistComponent') |
13 | 14 | ||
15 | type PlaylistElement = { | ||
16 | enabled: boolean | ||
17 | playlistElementId?: number | ||
18 | startTimestamp?: number | ||
19 | stopTimestamp?: number | ||
20 | } | ||
21 | |||
14 | type PlaylistSummary = { | 22 | type PlaylistSummary = { |
15 | id: number | 23 | id: number |
16 | inPlaylist: boolean | ||
17 | displayName: string | 24 | displayName: string |
25 | optionalRowDisplayed: boolean | ||
18 | 26 | ||
19 | playlistElementId?: number | 27 | elements: PlaylistElement[] |
20 | startTimestamp?: number | ||
21 | stopTimestamp?: number | ||
22 | } | 28 | } |
23 | 29 | ||
24 | @Component({ | 30 | @Component({ |
@@ -33,16 +39,11 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
33 | @Input() lazyLoad = false | 39 | @Input() lazyLoad = false |
34 | 40 | ||
35 | isNewPlaylistBlockOpened = false | 41 | isNewPlaylistBlockOpened = false |
42 | |||
36 | videoPlaylistSearch: string | 43 | videoPlaylistSearch: string |
37 | videoPlaylistSearchChanged = new Subject<string>() | 44 | videoPlaylistSearchChanged = new Subject<string>() |
45 | |||
38 | videoPlaylists: PlaylistSummary[] = [] | 46 | videoPlaylists: PlaylistSummary[] = [] |
39 | timestampOptions: { | ||
40 | startTimestampEnabled: boolean | ||
41 | startTimestamp: number | ||
42 | stopTimestampEnabled: boolean | ||
43 | stopTimestamp: number | ||
44 | } | ||
45 | displayOptions = false | ||
46 | 47 | ||
47 | private disabled = false | 48 | private disabled = false |
48 | 49 | ||
@@ -106,7 +107,6 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
106 | this.videoPlaylists = [] | 107 | this.videoPlaylists = [] |
107 | this.videoPlaylistSearch = undefined | 108 | this.videoPlaylistSearch = undefined |
108 | 109 | ||
109 | this.resetOptions(true) | ||
110 | this.load() | 110 | this.load() |
111 | 111 | ||
112 | this.cd.markForCheck() | 112 | this.cd.markForCheck() |
@@ -115,7 +115,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
115 | load () { | 115 | load () { |
116 | logger('Loading component') | 116 | logger('Loading component') |
117 | 117 | ||
118 | this.listenToPlaylistChanges() | 118 | this.listenToVideoPlaylistChange() |
119 | 119 | ||
120 | this.videoPlaylistService.listMyPlaylistWithCache(this.user, this.videoPlaylistSearch) | 120 | this.videoPlaylistService.listMyPlaylistWithCache(this.user, this.videoPlaylistSearch) |
121 | .subscribe(playlistsResult => { | 121 | .subscribe(playlistsResult => { |
@@ -128,7 +128,6 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
128 | openChange (opened: boolean) { | 128 | openChange (opened: boolean) { |
129 | if (opened === false) { | 129 | if (opened === false) { |
130 | this.isNewPlaylistBlockOpened = false | 130 | this.isNewPlaylistBlockOpened = false |
131 | this.displayOptions = false | ||
132 | } | 131 | } |
133 | } | 132 | } |
134 | 133 | ||
@@ -138,17 +137,49 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
138 | this.isNewPlaylistBlockOpened = true | 137 | this.isNewPlaylistBlockOpened = true |
139 | } | 138 | } |
140 | 139 | ||
141 | togglePlaylist (event: Event, playlist: PlaylistSummary) { | 140 | toggleMainPlaylist (e: Event, playlist: PlaylistSummary) { |
142 | event.preventDefault() | 141 | e.preventDefault() |
142 | |||
143 | if (this.isPresentMultipleTimes(playlist) || playlist.optionalRowDisplayed) return | ||
143 | 144 | ||
144 | if (playlist.inPlaylist === true) { | 145 | if (playlist.elements.length === 0) { |
145 | this.removeVideoFromPlaylist(playlist) | 146 | const element: PlaylistElement = { |
147 | enabled: true, | ||
148 | playlistElementId: undefined, | ||
149 | startTimestamp: 0, | ||
150 | stopTimestamp: this.video.duration | ||
151 | } | ||
152 | |||
153 | this.addVideoInPlaylist(playlist, element) | ||
146 | } else { | 154 | } else { |
147 | this.addVideoInPlaylist(playlist) | 155 | this.removeVideoFromPlaylist(playlist, playlist.elements[0].playlistElementId) |
156 | playlist.elements = [] | ||
148 | } | 157 | } |
149 | 158 | ||
150 | playlist.inPlaylist = !playlist.inPlaylist | 159 | this.cd.markForCheck() |
151 | this.resetOptions() | 160 | } |
161 | |||
162 | toggleOptionalPlaylist (e: Event, playlist: PlaylistSummary, element: PlaylistElement, startTimestamp: number, stopTimestamp: number) { | ||
163 | e.preventDefault() | ||
164 | |||
165 | if (element.enabled) { | ||
166 | this.removeVideoFromPlaylist(playlist, element.playlistElementId) | ||
167 | element.enabled = false | ||
168 | |||
169 | // Hide optional rows pane when the user unchecked all the playlists | ||
170 | if (this.isPrimaryCheckboxChecked(playlist) === false) { | ||
171 | playlist.optionalRowDisplayed = false | ||
172 | } | ||
173 | } else { | ||
174 | const element: PlaylistElement = { | ||
175 | enabled: true, | ||
176 | playlistElementId: undefined, | ||
177 | startTimestamp, | ||
178 | stopTimestamp | ||
179 | } | ||
180 | |||
181 | this.addVideoInPlaylist(playlist, element) | ||
182 | } | ||
152 | 183 | ||
153 | this.cd.markForCheck() | 184 | this.cd.markForCheck() |
154 | } | 185 | } |
@@ -172,34 +203,99 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
172 | ) | 203 | ) |
173 | } | 204 | } |
174 | 205 | ||
175 | resetOptions (resetTimestamp = false) { | 206 | onVideoPlaylistSearchChanged () { |
176 | this.displayOptions = false | 207 | this.videoPlaylistSearchChanged.next() |
208 | } | ||
177 | 209 | ||
178 | this.timestampOptions = {} as any | 210 | isPrimaryCheckboxChecked (playlist: PlaylistSummary) { |
179 | this.timestampOptions.startTimestampEnabled = false | 211 | return playlist.elements.filter(e => e.enabled) |
180 | this.timestampOptions.stopTimestampEnabled = false | 212 | .length !== 0 |
213 | } | ||
181 | 214 | ||
182 | if (resetTimestamp) { | 215 | toggleOptionalRow (playlist: PlaylistSummary) { |
183 | this.timestampOptions.startTimestamp = 0 | 216 | playlist.optionalRowDisplayed = !playlist.optionalRowDisplayed |
184 | this.timestampOptions.stopTimestamp = this.video.duration | 217 | |
185 | } | 218 | this.cd.markForCheck() |
186 | } | 219 | } |
187 | 220 | ||
188 | formatTimestamp (playlist: PlaylistSummary) { | 221 | getPrimaryInputName (playlist: PlaylistSummary) { |
189 | const start = playlist.startTimestamp ? secondsToTime(playlist.startTimestamp) : '' | 222 | return 'in-playlist-primary-' + playlist.id |
190 | const stop = playlist.stopTimestamp ? secondsToTime(playlist.stopTimestamp) : '' | 223 | } |
191 | 224 | ||
192 | return `(${start}-${stop})` | 225 | getOptionalInputName (playlist: PlaylistSummary, element?: PlaylistElement) { |
226 | const suffix = element | ||
227 | ? '-' + element.playlistElementId | ||
228 | : '' | ||
229 | |||
230 | return 'in-playlist-optional-' + playlist.id + suffix | ||
193 | } | 231 | } |
194 | 232 | ||
195 | onVideoPlaylistSearchChanged () { | 233 | buildOptionalRowElements (playlist: PlaylistSummary) { |
196 | this.videoPlaylistSearchChanged.next() | 234 | const elements = playlist.elements |
235 | |||
236 | const lastElement = elements.length === 0 | ||
237 | ? undefined | ||
238 | : elements[elements.length - 1] | ||
239 | |||
240 | // Build an empty last element | ||
241 | if (!lastElement || lastElement.enabled === true) { | ||
242 | elements.push({ | ||
243 | enabled: false, | ||
244 | startTimestamp: 0, | ||
245 | stopTimestamp: this.video.duration | ||
246 | }) | ||
247 | } | ||
248 | |||
249 | return elements | ||
250 | } | ||
251 | |||
252 | isPresentMultipleTimes (playlist: PlaylistSummary) { | ||
253 | return playlist.elements.filter(e => e.enabled === true).length > 1 | ||
254 | } | ||
255 | |||
256 | onElementTimestampUpdate (playlist: PlaylistSummary, element: PlaylistElement) { | ||
257 | if (!element.playlistElementId || element.enabled === false) return | ||
258 | |||
259 | const body: VideoPlaylistElementUpdate = { | ||
260 | startTimestamp: element.startTimestamp, | ||
261 | stopTimestamp: element.stopTimestamp | ||
262 | } | ||
263 | |||
264 | this.videoPlaylistService.updateVideoOfPlaylist(playlist.id, element.playlistElementId, body, this.video.id) | ||
265 | .subscribe( | ||
266 | () => { | ||
267 | this.notifier.success($localize`Timestamps updated`) | ||
268 | }, | ||
269 | |||
270 | err => { | ||
271 | this.notifier.error(err.message) | ||
272 | }, | ||
273 | |||
274 | () => this.cd.markForCheck() | ||
275 | ) | ||
197 | } | 276 | } |
198 | 277 | ||
199 | private removeVideoFromPlaylist (playlist: PlaylistSummary) { | 278 | private isOptionalRowDisplayed (playlist: PlaylistSummary) { |
200 | if (!playlist.playlistElementId) return | 279 | const elements = playlist.elements.filter(e => e.enabled) |
280 | |||
281 | if (elements.length > 1) return true | ||
201 | 282 | ||
202 | this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, playlist.playlistElementId, this.video.id) | 283 | if (elements.length === 1) { |
284 | const element = elements[0] | ||
285 | |||
286 | if ( | ||
287 | (element.startTimestamp && element.startTimestamp !== 0) || | ||
288 | (element.stopTimestamp && element.stopTimestamp !== this.video.duration) | ||
289 | ) { | ||
290 | return true | ||
291 | } | ||
292 | } | ||
293 | |||
294 | return false | ||
295 | } | ||
296 | |||
297 | private removeVideoFromPlaylist (playlist: PlaylistSummary, elementId: number) { | ||
298 | this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, elementId, this.video.id) | ||
203 | .subscribe( | 299 | .subscribe( |
204 | () => { | 300 | () => { |
205 | this.notifier.success($localize`Video removed from ${playlist.displayName}`) | 301 | this.notifier.success($localize`Video removed from ${playlist.displayName}`) |
@@ -213,7 +309,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
213 | ) | 309 | ) |
214 | } | 310 | } |
215 | 311 | ||
216 | private listenToPlaylistChanges () { | 312 | private listenToVideoPlaylistChange () { |
217 | this.unsubscribePlaylistChanges() | 313 | this.unsubscribePlaylistChanges() |
218 | 314 | ||
219 | this.listenToPlaylistChangeSub = this.videoPlaylistService.listenToVideoPlaylistChange(this.video.id) | 315 | this.listenToPlaylistChangeSub = this.videoPlaylistService.listenToVideoPlaylistChange(this.video.id) |
@@ -231,18 +327,30 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
231 | private rebuildPlaylists (existResult: VideoExistInPlaylist[]) { | 327 | private rebuildPlaylists (existResult: VideoExistInPlaylist[]) { |
232 | logger('Got existing results for %d.', this.video.id, existResult) | 328 | logger('Got existing results for %d.', this.video.id, existResult) |
233 | 329 | ||
330 | const oldPlaylists = this.videoPlaylists | ||
331 | |||
234 | this.videoPlaylists = [] | 332 | this.videoPlaylists = [] |
235 | for (const playlist of this.playlistsData) { | 333 | for (const playlist of this.playlistsData) { |
236 | const existingPlaylist = existResult.find(p => p.playlistId === playlist.id) | 334 | const existingPlaylists = existResult.filter(p => p.playlistId === playlist.id) |
237 | 335 | ||
238 | this.videoPlaylists.push({ | 336 | const playlistSummary = { |
239 | id: playlist.id, | 337 | id: playlist.id, |
338 | optionalRowDisplayed: false, | ||
240 | displayName: playlist.displayName, | 339 | displayName: playlist.displayName, |
241 | inPlaylist: !!existingPlaylist, | 340 | elements: existingPlaylists.map(e => ({ |
242 | playlistElementId: existingPlaylist ? existingPlaylist.playlistElementId : undefined, | 341 | enabled: true, |
243 | startTimestamp: existingPlaylist ? existingPlaylist.startTimestamp : undefined, | 342 | playlistElementId: e.playlistElementId, |
244 | stopTimestamp: existingPlaylist ? existingPlaylist.stopTimestamp : undefined | 343 | startTimestamp: e.startTimestamp || 0, |
245 | }) | 344 | stopTimestamp: e.stopTimestamp || this.video.duration |
345 | })) | ||
346 | } | ||
347 | |||
348 | const oldPlaylist = oldPlaylists.find(p => p.id === playlist.id) | ||
349 | playlistSummary.optionalRowDisplayed = oldPlaylist | ||
350 | ? oldPlaylist.optionalRowDisplayed | ||
351 | : this.isOptionalRowDisplayed(playlistSummary) | ||
352 | |||
353 | this.videoPlaylists.push(playlistSummary) | ||
246 | } | 354 | } |
247 | 355 | ||
248 | logger('Rebuilt playlist state for video %d.', this.video.id, this.videoPlaylists) | 356 | logger('Rebuilt playlist state for video %d.', this.video.id, this.videoPlaylists) |
@@ -250,20 +358,22 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
250 | this.cd.markForCheck() | 358 | this.cd.markForCheck() |
251 | } | 359 | } |
252 | 360 | ||
253 | private addVideoInPlaylist (playlist: PlaylistSummary) { | 361 | private addVideoInPlaylist (playlist: PlaylistSummary, element: PlaylistElement) { |
254 | const body: VideoPlaylistElementCreate = { videoId: this.video.id } | 362 | const body: VideoPlaylistElementCreate = { videoId: this.video.id } |
255 | 363 | ||
256 | if (this.timestampOptions.startTimestampEnabled) body.startTimestamp = this.timestampOptions.startTimestamp | 364 | if (element.startTimestamp) body.startTimestamp = element.startTimestamp |
257 | if (this.timestampOptions.stopTimestampEnabled) body.stopTimestamp = this.timestampOptions.stopTimestamp | 365 | if (element.stopTimestamp && element.stopTimestamp !== this.video.duration) body.stopTimestamp = element.stopTimestamp |
258 | 366 | ||
259 | this.videoPlaylistService.addVideoInPlaylist(playlist.id, body) | 367 | this.videoPlaylistService.addVideoInPlaylist(playlist.id, body) |
260 | .subscribe( | 368 | .subscribe( |
261 | () => { | 369 | res => { |
262 | const message = body.startTimestamp || body.stopTimestamp | 370 | const message = body.startTimestamp || body.stopTimestamp |
263 | ? $localize`Video added in ${playlist.displayName} at timestamps ${this.formatTimestamp(playlist)}` | 371 | ? $localize`Video added in ${playlist.displayName} at timestamps ${this.formatTimestamp(element)}` |
264 | : $localize`Video added in ${playlist.displayName}` | 372 | : $localize`Video added in ${playlist.displayName}` |
265 | 373 | ||
266 | this.notifier.success(message) | 374 | this.notifier.success(message) |
375 | |||
376 | if (element) element.playlistElementId = res.videoPlaylistElement.id | ||
267 | }, | 377 | }, |
268 | 378 | ||
269 | err => { | 379 | err => { |
@@ -273,4 +383,11 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
273 | () => this.cd.markForCheck() | 383 | () => this.cd.markForCheck() |
274 | ) | 384 | ) |
275 | } | 385 | } |
386 | |||
387 | private formatTimestamp (element: PlaylistElement) { | ||
388 | const start = element.startTimestamp ? secondsToTime(element.startTimestamp) : '' | ||
389 | const stop = element.stopTimestamp ? secondsToTime(element.stopTimestamp) : '' | ||
390 | |||
391 | return `(${start}-${stop})` | ||
392 | } | ||
276 | } | 393 | } |