Reorganize client shared modules
[oweals/peertube.git] / client / src / app / videos / +video-edit / video-update.component.ts
1 import { map, switchMap } from 'rxjs/operators'
2 import { Component, HostListener, OnInit } from '@angular/core'
3 import { ActivatedRoute, Router } from '@angular/router'
4 import { Notifier } from '@app/core'
5 import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
6 import { VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main'
7 import { LoadingBarService } from '@ngx-loading-bar/core'
8 import { I18n } from '@ngx-translate/i18n-polyfill'
9 import { VideoPrivacy } from '@shared/models'
10
11 @Component({
12   selector: 'my-videos-update',
13   styleUrls: [ './shared/video-edit.component.scss' ],
14   templateUrl: './video-update.component.html'
15 })
16 export class VideoUpdateComponent extends FormReactive implements OnInit {
17   video: VideoEdit
18
19   isUpdatingVideo = false
20   userVideoChannels: { id: number, label: string, support: string }[] = []
21   schedulePublicationPossible = false
22   videoCaptions: VideoCaptionEdit[] = []
23   waitTranscodingEnabled = true
24
25   private updateDone = false
26
27   constructor (
28     protected formValidatorService: FormValidatorService,
29     private route: ActivatedRoute,
30     private router: Router,
31     private notifier: Notifier,
32     private videoService: VideoService,
33     private loadingBar: LoadingBarService,
34     private videoCaptionService: VideoCaptionService,
35     private i18n: I18n
36   ) {
37     super()
38   }
39
40   ngOnInit () {
41     this.buildForm({})
42
43     this.route.data
44         .pipe(map(data => data.videoData))
45         .subscribe(({ video, videoChannels, videoCaptions }) => {
46           this.video = new VideoEdit(video)
47           this.userVideoChannels = videoChannels
48           this.videoCaptions = videoCaptions
49
50           this.schedulePublicationPossible = this.video.privacy === VideoPrivacy.PRIVATE
51
52           const videoFiles = (video as VideoDetails).getFiles()
53           if (videoFiles.length > 1) { // Already transcoded
54             this.waitTranscodingEnabled = false
55           }
56
57           // FIXME: Angular does not detect the change inside this subscription, so use the patched setTimeout
58           setTimeout(() => this.hydrateFormFromVideo())
59         },
60
61         err => {
62           console.error(err)
63           this.notifier.error(err.message)
64         }
65       )
66   }
67
68   @HostListener('window:beforeunload', [ '$event' ])
69   onUnload (event: any) {
70     const { text, canDeactivate } = this.canDeactivate()
71
72     if (canDeactivate) return
73
74     event.returnValue = text
75     return text
76   }
77
78   canDeactivate (): { canDeactivate: boolean, text?: string } {
79     if (this.updateDone === true) return { canDeactivate: true }
80
81     const text = this.i18n('You have unsaved changes! If you leave, your changes will be lost.')
82
83     for (const caption of this.videoCaptions) {
84       if (caption.action) return { canDeactivate: false, text }
85     }
86
87     return { canDeactivate: this.formChanged === false, text }
88   }
89
90   checkForm () {
91     this.forceCheck()
92
93     return this.form.valid
94   }
95
96   update () {
97     if (this.checkForm() === false
98       || this.isUpdatingVideo === true) {
99       return
100     }
101
102     this.video.patch(this.form.value)
103
104     this.loadingBar.start()
105     this.isUpdatingVideo = true
106
107     // Update the video
108     this.videoService.updateVideo(this.video)
109         .pipe(
110           // Then update captions
111           switchMap(() => this.videoCaptionService.updateCaptions(this.video.id, this.videoCaptions))
112         )
113         .subscribe(
114           () => {
115             this.updateDone = true
116             this.isUpdatingVideo = false
117             this.loadingBar.complete()
118             this.notifier.success(this.i18n('Video updated.'))
119             this.router.navigate([ '/videos/watch', this.video.uuid ])
120           },
121
122           err => {
123             this.loadingBar.complete()
124             this.isUpdatingVideo = false
125             this.notifier.error(err.message)
126             console.error(err)
127           }
128         )
129   }
130
131   private hydrateFormFromVideo () {
132     this.form.patchValue(this.video.toFormPatch())
133
134     const objects = [
135       {
136         url: 'thumbnailUrl',
137         name: 'thumbnailfile'
138       },
139       {
140         url: 'previewUrl',
141         name: 'previewfile'
142       }
143     ]
144
145     for (const obj of objects) {
146       fetch(this.video[obj.url])
147         .then(response => response.blob())
148         .then(data => {
149           this.form.patchValue({
150             [ obj.name ]: data
151           })
152         })
153     }
154   }
155 }