aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-12-22 18:02:36 +0100
committerChocobozzz <me@florianbigard.com>2021-12-29 10:10:01 +0100
commit3c065fe3b3e1385d59ad1980251d14b712648155 (patch)
treef5f7f1b428b8155a735014304e2d45b9ed92fe07 /client/src/app
parent61cc1c03bf6f12af7c1b2e2a7d2fdaa563c37b59 (diff)
downloadPeerTube-3c065fe3b3e1385d59ad1980251d14b712648155.tar.gz
PeerTube-3c065fe3b3e1385d59ad1980251d14b712648155.tar.zst
PeerTube-3c065fe3b3e1385d59ad1980251d14b712648155.zip
Enhance plugin video fields
Add video form tab selection Add ability to display an error
Diffstat (limited to 'client/src/app')
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.html13
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.ts55
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts4
-rw-r--r--client/src/app/shared/shared-forms/form-reactive.ts19
-rw-r--r--client/src/app/shared/shared-forms/form-validator.service.ts10
5 files changed, 77 insertions, 24 deletions
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.html b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
index aa88d6c4c..f65cd8883 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.html
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
@@ -146,6 +146,13 @@
146 </ng-template> 146 </ng-template>
147 </my-peertube-checkbox> 147 </my-peertube-checkbox>
148 148
149 <ng-container ngbNavItem *ngIf="getPluginsFields('main').length !== 0">
150
151 <div *ngFor="let pluginSetting of getPluginsFields('main')" class="form-group" [hidden]="isPluginFieldHidden(pluginSetting)">
152 <my-dynamic-form-field [form]="pluginDataFormGroup" [formErrors]="formErrors['pluginData']" [setting]="pluginSetting.commonOptions"></my-dynamic-form-field>
153 </div>
154
155 </ng-container>
149 </div> 156 </div>
150 </div> 157 </div>
151 </ng-template> 158 </ng-template>
@@ -339,15 +346,15 @@
339 </ng-template> 346 </ng-template>
340 </ng-container> 347 </ng-container>
341 348
342 <ng-container ngbNavItem *ngIf="pluginFields.length !== 0"> 349 <ng-container ngbNavItem *ngIf="getPluginsFields('plugin-settings').length !== 0">
343 <a ngbNavLink i18n>Plugin settings</a> 350 <a ngbNavLink i18n>Plugin settings</a>
344 351
345 <ng-template ngbNavContent> 352 <ng-template ngbNavContent>
346 <div class="row plugin-settings"> 353 <div class="row plugin-settings">
347 354
348 <div class="col-md-12 col-xl-8"> 355 <div class="col-md-12 col-xl-8">
349 <div *ngFor="let pluginSetting of pluginFields" class="form-group" [hidden]="isPluginFieldHidden(pluginSetting)"> 356 <div *ngFor="let pluginSetting of getPluginsFields('plugin-settings')" class="form-group" [hidden]="isPluginFieldHidden(pluginSetting)">
350 <my-dynamic-form-field [form]="pluginDataFormGroup" [formErrors]="formErrors" [setting]="pluginSetting.commonOptions"></my-dynamic-form-field> 357 <my-dynamic-form-field [form]="pluginDataFormGroup" [formErrors]="formErrors['pluginData']" [setting]="pluginSetting.commonOptions"></my-dynamic-form-field>
351 </div> 358 </div>
352 </div> 359 </div>
353 360
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
index 2bec933e9..a03005bcb 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
@@ -1,10 +1,11 @@
1import { forkJoin } from 'rxjs' 1import { forkJoin } from 'rxjs'
2import { map } from 'rxjs/operators' 2import { map } from 'rxjs/operators'
3import { SelectChannelItem } from 'src/types/select-options-item.model' 3import { SelectChannelItem } from 'src/types/select-options-item.model'
4import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' 4import { ChangeDetectorRef, Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'
5import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms' 5import { AbstractControl, FormArray, FormControl, FormGroup, ValidationErrors, Validators } from '@angular/forms'
6import { HooksService, PluginService, ServerService } from '@app/core' 6import { HooksService, PluginService, ServerService } from '@app/core'
7import { removeElementFromArray } from '@app/helpers' 7import { removeElementFromArray } from '@app/helpers'
8import { BuildFormValidator } from '@app/shared/form-validators'
8import { 9import {
9 VIDEO_CATEGORY_VALIDATOR, 10 VIDEO_CATEGORY_VALIDATOR,
10 VIDEO_CHANNEL_VALIDATOR, 11 VIDEO_CHANNEL_VALIDATOR,
@@ -101,7 +102,8 @@ export class VideoEditComponent implements OnInit, OnDestroy {
101 private instanceService: InstanceService, 102 private instanceService: InstanceService,
102 private i18nPrimengCalendarService: I18nPrimengCalendarService, 103 private i18nPrimengCalendarService: I18nPrimengCalendarService,
103 private ngZone: NgZone, 104 private ngZone: NgZone,
104 private hooks: HooksService 105 private hooks: HooksService,
106 private cd: ChangeDetectorRef
105 ) { 107 ) {
106 this.calendarTimezone = this.i18nPrimengCalendarService.getTimezone() 108 this.calendarTimezone = this.i18nPrimengCalendarService.getTimezone()
107 this.calendarDateFormat = this.i18nPrimengCalendarService.getDateFormat() 109 this.calendarDateFormat = this.i18nPrimengCalendarService.getDateFormat()
@@ -116,7 +118,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
116 licence: this.serverConfig.defaults.publish.licence, 118 licence: this.serverConfig.defaults.publish.licence,
117 tags: [] 119 tags: []
118 } 120 }
119 const obj: any = { 121 const obj: { [ id: string ]: BuildFormValidator } = {
120 name: VIDEO_NAME_VALIDATOR, 122 name: VIDEO_NAME_VALIDATOR,
121 privacy: VIDEO_PRIVACY_VALIDATOR, 123 privacy: VIDEO_PRIVACY_VALIDATOR,
122 channelId: VIDEO_CHANNEL_VALIDATOR, 124 channelId: VIDEO_CHANNEL_VALIDATOR,
@@ -138,7 +140,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
138 saveReplay: null 140 saveReplay: null
139 } 141 }
140 142
141 this.formValidatorService.updateForm( 143 this.formValidatorService.updateFormGroup(
142 this.form, 144 this.form,
143 this.formErrors, 145 this.formErrors,
144 this.validationMessages, 146 this.validationMessages,
@@ -275,6 +277,14 @@ export class VideoEditComponent implements OnInit, OnDestroy {
275 }) 277 })
276 } 278 }
277 279
280 getPluginsFields (tab: 'main' | 'plugin-settings') {
281 return this.pluginFields.filter(p => {
282 const wanted = p.videoFormOptions.tab ?? 'plugin-settings'
283
284 return wanted === tab
285 })
286 }
287
278 private sortVideoCaptions () { 288 private sortVideoCaptions () {
279 this.videoCaptions.sort((v1, v2) => { 289 this.videoCaptions.sort((v1, v2) => {
280 if (v1.language.label < v2.language.label) return -1 290 if (v1.language.label < v2.language.label) return -1
@@ -289,15 +299,44 @@ export class VideoEditComponent implements OnInit, OnDestroy {
289 299
290 if (this.pluginFields.length === 0) return 300 if (this.pluginFields.length === 0) return
291 301
292 const obj: any = {} 302 const pluginObj: { [ id: string ]: BuildFormValidator } = {}
303 const pluginValidationMessages: FormReactiveValidationMessages = {}
304 const pluginFormErrors: any = {}
305 const pluginDefaults: any = {}
293 306
294 for (const setting of this.pluginFields) { 307 for (const setting of this.pluginFields) {
295 obj[setting.commonOptions.name] = new FormControl(setting.commonOptions.default) 308 const validator = (control: AbstractControl): ValidationErrors | null => {
309 if (!setting.commonOptions.error) return null
310
311 const error = setting.commonOptions.error({ formValues: this.form.value, value: control.value })
312
313 return error?.error ? { [setting.commonOptions.name]: error.text } : null
314 }
315
316 const name = setting.commonOptions.name
317
318 pluginObj[name] = {
319 VALIDATORS: [ validator ],
320 MESSAGES: {}
321 }
322
323 pluginDefaults[name] = setting.commonOptions.default
296 } 324 }
297 325
298 this.pluginDataFormGroup = new FormGroup(obj) 326 this.pluginDataFormGroup = new FormGroup({})
327 this.formValidatorService.updateFormGroup(
328 this.pluginDataFormGroup,
329 pluginFormErrors,
330 pluginValidationMessages,
331 pluginObj,
332 pluginDefaults
333 )
334
299 this.form.addControl('pluginData', this.pluginDataFormGroup) 335 this.form.addControl('pluginData', this.pluginDataFormGroup)
336 this.formErrors['pluginData'] = pluginFormErrors
337 this.validationMessages['pluginData'] = pluginValidationMessages
300 338
339 this.cd.detectChanges()
301 this.pluginFieldsAdded.emit() 340 this.pluginFieldsAdded.emit()
302 } 341 }
303 342
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
index 76f154249..fa5800897 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
@@ -226,7 +226,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
226 } 226 }
227 227
228 isPublishingButtonDisabled () { 228 isPublishingButtonDisabled () {
229 return !this.form.valid || 229 return !this.checkForm() ||
230 this.isUpdatingVideo === true || 230 this.isUpdatingVideo === true ||
231 this.videoUploaded !== true || 231 this.videoUploaded !== true ||
232 !this.videoUploadedIds.id 232 !this.videoUploadedIds.id
@@ -240,7 +240,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
240 } 240 }
241 241
242 updateSecondStep () { 242 updateSecondStep () {
243 if (this.isPublishingButtonDisabled() || !this.checkForm()) { 243 if (this.isPublishingButtonDisabled()) {
244 return 244 return
245 } 245 }
246 246
diff --git a/client/src/app/shared/shared-forms/form-reactive.ts b/client/src/app/shared/shared-forms/form-reactive.ts
index f2ce82360..30b59c141 100644
--- a/client/src/app/shared/shared-forms/form-reactive.ts
+++ b/client/src/app/shared/shared-forms/form-reactive.ts
@@ -56,13 +56,18 @@ export abstract class FormReactive {
56 56
57 if (control.dirty) this.formChanged = true 57 if (control.dirty) this.formChanged = true
58 58
59 // Don't care if dirty on force check 59 if (forceCheck) control.updateValueAndValidity({ emitEvent: false })
60 const isDirty = control.dirty || forceCheck === true 60 if (!control || !control.dirty || !control.enabled || control.valid) continue
61 if (control && isDirty && control.enabled && !control.valid) { 61
62 const messages = validationMessages[field] 62 const staticMessages = validationMessages[field]
63 for (const key of Object.keys(control.errors)) { 63 for (const key of Object.keys(control.errors)) {
64 formErrors[field] += messages[key] + ' ' 64 const formErrorValue = control.errors[key]
65 } 65
66 // Try to find error message in static validation messages first
67 // Then check if the validator returns a string that is the error
68 if (typeof formErrorValue === 'boolean') formErrors[field] += staticMessages[key] + ' '
69 else if (typeof formErrorValue === 'string') formErrors[field] += control.errors[key]
70 else throw new Error('Form error value of ' + field + ' is invalid')
66 } 71 }
67 } 72 }
68 } 73 }
diff --git a/client/src/app/shared/shared-forms/form-validator.service.ts b/client/src/app/shared/shared-forms/form-validator.service.ts
index c0664de5f..055fbb2d9 100644
--- a/client/src/app/shared/shared-forms/form-validator.service.ts
+++ b/client/src/app/shared/shared-forms/form-validator.service.ts
@@ -40,7 +40,7 @@ export class FormValidatorService {
40 return { form, formErrors, validationMessages } 40 return { form, formErrors, validationMessages }
41 } 41 }
42 42
43 updateForm ( 43 updateFormGroup (
44 form: FormGroup, 44 form: FormGroup,
45 formErrors: FormReactiveErrors, 45 formErrors: FormReactiveErrors,
46 validationMessages: FormReactiveValidationMessages, 46 validationMessages: FormReactiveValidationMessages,
@@ -52,7 +52,7 @@ export class FormValidatorService {
52 52
53 const field = obj[name] 53 const field = obj[name]
54 if (this.isRecursiveField(field)) { 54 if (this.isRecursiveField(field)) {
55 this.updateForm( 55 this.updateFormGroup(
56 form[name], 56 form[name],
57 formErrors[name] as FormReactiveErrors, 57 formErrors[name] as FormReactiveErrors,
58 validationMessages[name] as FormReactiveValidationMessages, 58 validationMessages[name] as FormReactiveValidationMessages,
@@ -66,8 +66,10 @@ export class FormValidatorService {
66 66
67 const defaultValue = defaultValues[name] || '' 67 const defaultValue = defaultValues[name] || ''
68 68
69 if (field?.VALIDATORS) form.addControl(name, new FormControl(defaultValue, field.VALIDATORS as ValidatorFn[])) 69 form.addControl(
70 else form.addControl(name, new FormControl(defaultValue)) 70 name,
71 new FormControl(defaultValue, field?.VALIDATORS as ValidatorFn[])
72 )
71 } 73 }
72 } 74 }
73 75