diff options
author | Chocobozzz <me@florianbigard.com> | 2020-06-23 14:49:20 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-06-23 16:00:49 +0200 |
commit | 1942f11d5ee6926ad93dc1b79fae18325ba5de18 (patch) | |
tree | 3f2a3cd9466a56c419d197ac832a3e9cbc86bec4 /client/src/app/+videos/+video-edit/shared/video-edit.component.ts | |
parent | 67ed6552b831df66713bac9e672738796128d33f (diff) | |
download | PeerTube-1942f11d5ee6926ad93dc1b79fae18325ba5de18.tar.gz PeerTube-1942f11d5ee6926ad93dc1b79fae18325ba5de18.tar.zst PeerTube-1942f11d5ee6926ad93dc1b79fae18325ba5de18.zip |
Lazy load all routes
Diffstat (limited to 'client/src/app/+videos/+video-edit/shared/video-edit.component.ts')
-rw-r--r-- | client/src/app/+videos/+video-edit/shared/video-edit.component.ts | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts new file mode 100644 index 000000000..239e453ad --- /dev/null +++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts | |||
@@ -0,0 +1,274 @@ | |||
1 | import { map } from 'rxjs/operators' | ||
2 | import { Component, Input, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' | ||
3 | import { FormArray, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms' | ||
4 | import { ServerService } from '@app/core' | ||
5 | import { removeElementFromArray } from '@app/helpers' | ||
6 | import { FormReactiveValidationMessages, FormValidatorService, VideoValidatorsService } from '@app/shared/shared-forms' | ||
7 | import { VideoCaptionEdit, VideoEdit, VideoService } from '@app/shared/shared-main' | ||
8 | import { ServerConfig, VideoConstant, VideoPrivacy } from '@shared/models' | ||
9 | import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service' | ||
10 | import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component' | ||
11 | |||
12 | @Component({ | ||
13 | selector: 'my-video-edit', | ||
14 | styleUrls: [ './video-edit.component.scss' ], | ||
15 | templateUrl: './video-edit.component.html' | ||
16 | }) | ||
17 | export class VideoEditComponent implements OnInit, OnDestroy { | ||
18 | @Input() form: FormGroup | ||
19 | @Input() formErrors: { [ id: string ]: string } = {} | ||
20 | @Input() validationMessages: FormReactiveValidationMessages = {} | ||
21 | @Input() userVideoChannels: { id: number, label: string, support: string }[] = [] | ||
22 | @Input() schedulePublicationPossible = true | ||
23 | @Input() videoCaptions: (VideoCaptionEdit & { captionPath?: string })[] = [] | ||
24 | @Input() waitTranscodingEnabled = true | ||
25 | |||
26 | @ViewChild('videoCaptionAddModal', { static: true }) videoCaptionAddModal: VideoCaptionAddModalComponent | ||
27 | |||
28 | // So that it can be accessed in the template | ||
29 | readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY | ||
30 | |||
31 | videoPrivacies: VideoConstant<VideoPrivacy>[] = [] | ||
32 | videoCategories: VideoConstant<number>[] = [] | ||
33 | videoLicences: VideoConstant<number>[] = [] | ||
34 | videoLanguages: VideoConstant<string>[] = [] | ||
35 | |||
36 | tagValidators: ValidatorFn[] | ||
37 | tagValidatorsMessages: { [ name: string ]: string } | ||
38 | |||
39 | schedulePublicationEnabled = false | ||
40 | |||
41 | calendarLocale: any = {} | ||
42 | minScheduledDate = new Date() | ||
43 | myYearRange = '1880:' + (new Date()).getFullYear() | ||
44 | |||
45 | calendarTimezone: string | ||
46 | calendarDateFormat: string | ||
47 | |||
48 | serverConfig: ServerConfig | ||
49 | |||
50 | private schedulerInterval: any | ||
51 | private firstPatchDone = false | ||
52 | private initialVideoCaptions: string[] = [] | ||
53 | |||
54 | constructor ( | ||
55 | private formValidatorService: FormValidatorService, | ||
56 | private videoValidatorsService: VideoValidatorsService, | ||
57 | private videoService: VideoService, | ||
58 | private serverService: ServerService, | ||
59 | private i18nPrimengCalendarService: I18nPrimengCalendarService, | ||
60 | private ngZone: NgZone | ||
61 | ) { | ||
62 | this.tagValidators = this.videoValidatorsService.VIDEO_TAGS.VALIDATORS | ||
63 | this.tagValidatorsMessages = this.videoValidatorsService.VIDEO_TAGS.MESSAGES | ||
64 | |||
65 | this.calendarLocale = this.i18nPrimengCalendarService.getCalendarLocale() | ||
66 | this.calendarTimezone = this.i18nPrimengCalendarService.getTimezone() | ||
67 | this.calendarDateFormat = this.i18nPrimengCalendarService.getDateFormat() | ||
68 | } | ||
69 | |||
70 | get existingCaptions () { | ||
71 | return this.videoCaptions | ||
72 | .filter(c => c.action !== 'REMOVE') | ||
73 | .map(c => c.language.id) | ||
74 | } | ||
75 | |||
76 | updateForm () { | ||
77 | const defaultValues: any = { | ||
78 | nsfw: 'false', | ||
79 | commentsEnabled: 'true', | ||
80 | downloadEnabled: 'true', | ||
81 | waitTranscoding: 'true', | ||
82 | tags: [] | ||
83 | } | ||
84 | const obj: any = { | ||
85 | name: this.videoValidatorsService.VIDEO_NAME, | ||
86 | privacy: this.videoValidatorsService.VIDEO_PRIVACY, | ||
87 | channelId: this.videoValidatorsService.VIDEO_CHANNEL, | ||
88 | nsfw: null, | ||
89 | commentsEnabled: null, | ||
90 | downloadEnabled: null, | ||
91 | waitTranscoding: null, | ||
92 | category: this.videoValidatorsService.VIDEO_CATEGORY, | ||
93 | licence: this.videoValidatorsService.VIDEO_LICENCE, | ||
94 | language: this.videoValidatorsService.VIDEO_LANGUAGE, | ||
95 | description: this.videoValidatorsService.VIDEO_DESCRIPTION, | ||
96 | tags: null, | ||
97 | previewfile: null, | ||
98 | support: this.videoValidatorsService.VIDEO_SUPPORT, | ||
99 | schedulePublicationAt: this.videoValidatorsService.VIDEO_SCHEDULE_PUBLICATION_AT, | ||
100 | originallyPublishedAt: this.videoValidatorsService.VIDEO_ORIGINALLY_PUBLISHED_AT | ||
101 | } | ||
102 | |||
103 | this.formValidatorService.updateForm( | ||
104 | this.form, | ||
105 | this.formErrors, | ||
106 | this.validationMessages, | ||
107 | obj, | ||
108 | defaultValues | ||
109 | ) | ||
110 | |||
111 | this.form.addControl('captions', new FormArray([ | ||
112 | new FormGroup({ | ||
113 | language: new FormControl(), | ||
114 | captionfile: new FormControl() | ||
115 | }) | ||
116 | ])) | ||
117 | |||
118 | this.trackChannelChange() | ||
119 | this.trackPrivacyChange() | ||
120 | } | ||
121 | |||
122 | ngOnInit () { | ||
123 | this.updateForm() | ||
124 | |||
125 | this.serverService.getVideoCategories() | ||
126 | .subscribe(res => this.videoCategories = res) | ||
127 | this.serverService.getVideoLicences() | ||
128 | .subscribe(res => this.videoLicences = res) | ||
129 | this.serverService.getVideoLanguages() | ||
130 | .subscribe(res => this.videoLanguages = res) | ||
131 | |||
132 | this.serverService.getVideoPrivacies() | ||
133 | .subscribe(privacies => this.videoPrivacies = this.videoService.explainedPrivacyLabels(privacies)) | ||
134 | |||
135 | this.serverConfig = this.serverService.getTmpConfig() | ||
136 | this.serverService.getConfig() | ||
137 | .subscribe(config => this.serverConfig = config) | ||
138 | |||
139 | this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id) | ||
140 | |||
141 | this.ngZone.runOutsideAngular(() => { | ||
142 | this.schedulerInterval = setInterval(() => this.minScheduledDate = new Date(), 1000 * 60) // Update every minute | ||
143 | }) | ||
144 | } | ||
145 | |||
146 | ngOnDestroy () { | ||
147 | if (this.schedulerInterval) clearInterval(this.schedulerInterval) | ||
148 | } | ||
149 | |||
150 | onCaptionAdded (caption: VideoCaptionEdit) { | ||
151 | const existingCaption = this.videoCaptions.find(c => c.language.id === caption.language.id) | ||
152 | |||
153 | // Replace existing caption? | ||
154 | if (existingCaption) { | ||
155 | Object.assign(existingCaption, caption, { action: 'CREATE' as 'CREATE' }) | ||
156 | } else { | ||
157 | this.videoCaptions.push( | ||
158 | Object.assign(caption, { action: 'CREATE' as 'CREATE' }) | ||
159 | ) | ||
160 | } | ||
161 | |||
162 | this.sortVideoCaptions() | ||
163 | } | ||
164 | |||
165 | async deleteCaption (caption: VideoCaptionEdit) { | ||
166 | // Caption recovers his former state | ||
167 | if (caption.action && this.initialVideoCaptions.indexOf(caption.language.id) !== -1) { | ||
168 | caption.action = undefined | ||
169 | return | ||
170 | } | ||
171 | |||
172 | // This caption is not on the server, just remove it from our array | ||
173 | if (caption.action === 'CREATE') { | ||
174 | removeElementFromArray(this.videoCaptions, caption) | ||
175 | return | ||
176 | } | ||
177 | |||
178 | caption.action = 'REMOVE' as 'REMOVE' | ||
179 | } | ||
180 | |||
181 | openAddCaptionModal () { | ||
182 | this.videoCaptionAddModal.show() | ||
183 | } | ||
184 | |||
185 | private sortVideoCaptions () { | ||
186 | this.videoCaptions.sort((v1, v2) => { | ||
187 | if (v1.language.label < v2.language.label) return -1 | ||
188 | if (v1.language.label === v2.language.label) return 0 | ||
189 | |||
190 | return 1 | ||
191 | }) | ||
192 | } | ||
193 | |||
194 | private trackPrivacyChange () { | ||
195 | // We will update the schedule input and the wait transcoding checkbox validators | ||
196 | this.form.controls[ 'privacy' ] | ||
197 | .valueChanges | ||
198 | .pipe(map(res => parseInt(res.toString(), 10))) | ||
199 | .subscribe( | ||
200 | newPrivacyId => { | ||
201 | |||
202 | this.schedulePublicationEnabled = newPrivacyId === this.SPECIAL_SCHEDULED_PRIVACY | ||
203 | |||
204 | // Value changed | ||
205 | const scheduleControl = this.form.get('schedulePublicationAt') | ||
206 | const waitTranscodingControl = this.form.get('waitTranscoding') | ||
207 | |||
208 | if (this.schedulePublicationEnabled) { | ||
209 | scheduleControl.setValidators([ Validators.required ]) | ||
210 | |||
211 | waitTranscodingControl.disable() | ||
212 | waitTranscodingControl.setValue(false) | ||
213 | } else { | ||
214 | scheduleControl.clearValidators() | ||
215 | |||
216 | waitTranscodingControl.enable() | ||
217 | |||
218 | // Do not update the control value on first patch (values come from the server) | ||
219 | if (this.firstPatchDone === true) { | ||
220 | waitTranscodingControl.setValue(true) | ||
221 | } | ||
222 | } | ||
223 | |||
224 | scheduleControl.updateValueAndValidity() | ||
225 | waitTranscodingControl.updateValueAndValidity() | ||
226 | |||
227 | this.firstPatchDone = true | ||
228 | |||
229 | } | ||
230 | ) | ||
231 | } | ||
232 | |||
233 | private trackChannelChange () { | ||
234 | // We will update the "support" field depending on the channel | ||
235 | this.form.controls[ 'channelId' ] | ||
236 | .valueChanges | ||
237 | .pipe(map(res => parseInt(res.toString(), 10))) | ||
238 | .subscribe( | ||
239 | newChannelId => { | ||
240 | const oldChannelId = parseInt(this.form.value[ 'channelId' ], 10) | ||
241 | |||
242 | // Not initialized yet | ||
243 | if (isNaN(newChannelId)) return | ||
244 | const newChannel = this.userVideoChannels.find(c => c.id === newChannelId) | ||
245 | if (!newChannel) return | ||
246 | |||
247 | // Wait support field update | ||
248 | setTimeout(() => { | ||
249 | const currentSupport = this.form.value[ 'support' ] | ||
250 | |||
251 | // First time we set the channel? | ||
252 | if (isNaN(oldChannelId) && !currentSupport) return this.updateSupportField(newChannel.support) | ||
253 | |||
254 | const oldChannel = this.userVideoChannels.find(c => c.id === oldChannelId) | ||
255 | if (!newChannel || !oldChannel) { | ||
256 | console.error('Cannot find new or old channel.') | ||
257 | return | ||
258 | } | ||
259 | |||
260 | // If the current support text is not the same than the old channel, the user updated it. | ||
261 | // We don't want the user to lose his text, so stop here | ||
262 | if (currentSupport && currentSupport !== oldChannel.support) return | ||
263 | |||
264 | // Update the support text with our new channel | ||
265 | this.updateSupportField(newChannel.support) | ||
266 | }) | ||
267 | } | ||
268 | ) | ||
269 | } | ||
270 | |||
271 | private updateSupportField (support: string) { | ||
272 | return this.form.patchValue({ support: support || '' }) | ||
273 | } | ||
274 | } | ||