aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared/forms
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-02-11 11:52:34 +0100
committerChocobozzz <me@florianbigard.com>2019-02-11 11:52:34 +0100
commit88108880bbdba473cfe36ecbebc1c3c4f972e102 (patch)
treeb242efb3b4f0d7e49d88f2d1f2063b5b3b0489c0 /client/src/app/shared/forms
parent53a94c7cfa8368da4cd248d65df8346905938f0c (diff)
parent9b712a2017e4ab3cf12cd6bd58278905520159d0 (diff)
downloadPeerTube-88108880bbdba473cfe36ecbebc1c3c4f972e102.tar.gz
PeerTube-88108880bbdba473cfe36ecbebc1c3c4f972e102.tar.zst
PeerTube-88108880bbdba473cfe36ecbebc1c3c4f972e102.zip
Merge branch 'develop' into pr/1217
Diffstat (limited to 'client/src/app/shared/forms')
-rw-r--r--client/src/app/shared/forms/form-reactive.ts48
-rw-r--r--client/src/app/shared/forms/form-validators/form-validator.service.ts33
-rw-r--r--client/src/app/shared/forms/form-validators/index.ts1
-rw-r--r--client/src/app/shared/forms/form-validators/instance-validators.service.ts48
-rw-r--r--client/src/app/shared/forms/form-validators/user-validators.service.ts24
-rw-r--r--client/src/app/shared/forms/form-validators/video-abuse-validators.service.ts8
-rw-r--r--client/src/app/shared/forms/form-validators/video-change-ownership-validators.service.ts15
-rw-r--r--client/src/app/shared/forms/form-validators/video-channel-validators.service.ts28
-rw-r--r--client/src/app/shared/forms/form-validators/video-validators.service.ts4
-rw-r--r--client/src/app/shared/forms/index.ts1
-rw-r--r--client/src/app/shared/forms/markdown-textarea.component.ts2
-rw-r--r--client/src/app/shared/forms/peertube-checkbox.component.html6
-rw-r--r--client/src/app/shared/forms/peertube-checkbox.component.scss6
-rw-r--r--client/src/app/shared/forms/peertube-checkbox.component.ts5
-rw-r--r--client/src/app/shared/forms/reactive-file.component.ts17
-rw-r--r--client/src/app/shared/forms/textarea-autoresize.directive.ts25
16 files changed, 205 insertions, 66 deletions
diff --git a/client/src/app/shared/forms/form-reactive.ts b/client/src/app/shared/forms/form-reactive.ts
index 0bb7d25e6..b9873af2c 100644
--- a/client/src/app/shared/forms/form-reactive.ts
+++ b/client/src/app/shared/forms/form-reactive.ts
@@ -1,11 +1,9 @@
1import { FormGroup } from '@angular/forms' 1import { FormGroup } from '@angular/forms'
2import { BuildFormArgument, BuildFormDefaultValues, FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' 2import { BuildFormArgument, BuildFormDefaultValues, FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
3 3
4export type FormReactiveErrors = { [ id: string ]: string } 4export type FormReactiveErrors = { [ id: string ]: string | FormReactiveErrors }
5export type FormReactiveValidationMessages = { 5export type FormReactiveValidationMessages = {
6 [ id: string ]: { 6 [ id: string ]: { [ name: string ]: string } | FormReactiveValidationMessages
7 [ name: string ]: string
8 }
9} 7}
10 8
11export abstract class FormReactive { 9export abstract class FormReactive {
@@ -13,7 +11,7 @@ export abstract class FormReactive {
13 protected formChanged = false 11 protected formChanged = false
14 12
15 form: FormGroup 13 form: FormGroup
16 formErrors: FormReactiveErrors 14 formErrors: any // To avoid casting in template because of string | FormReactiveErrors
17 validationMessages: FormReactiveValidationMessages 15 validationMessages: FormReactiveValidationMessages
18 16
19 buildForm (obj: BuildFormArgument, defaultValues: BuildFormDefaultValues = {}) { 17 buildForm (obj: BuildFormArgument, defaultValues: BuildFormDefaultValues = {}) {
@@ -23,29 +21,49 @@ export abstract class FormReactive {
23 this.formErrors = formErrors 21 this.formErrors = formErrors
24 this.validationMessages = validationMessages 22 this.validationMessages = validationMessages
25 23
26 this.form.valueChanges.subscribe(() => this.onValueChanged(false)) 24 this.form.valueChanges.subscribe(() => this.onValueChanged(this.form, this.formErrors, this.validationMessages, false))
25 }
26
27 protected forceCheck () {
28 return this.onValueChanged(this.form, this.formErrors, this.validationMessages, true)
29 }
30
31 protected check () {
32 return this.onValueChanged(this.form, this.formErrors, this.validationMessages, false)
27 } 33 }
28 34
29 protected onValueChanged (forceCheck = false) { 35 private onValueChanged (
30 for (const field in this.formErrors) { 36 form: FormGroup,
37 formErrors: FormReactiveErrors,
38 validationMessages: FormReactiveValidationMessages,
39 forceCheck = false
40 ) {
41 for (const field of Object.keys(formErrors)) {
42 if (formErrors[field] && typeof formErrors[field] === 'object') {
43 this.onValueChanged(
44 form.controls[field] as FormGroup,
45 formErrors[field] as FormReactiveErrors,
46 validationMessages[field] as FormReactiveValidationMessages,
47 forceCheck
48 )
49 continue
50 }
51
31 // clear previous error message (if any) 52 // clear previous error message (if any)
32 this.formErrors[ field ] = '' 53 formErrors[ field ] = ''
33 const control = this.form.get(field) 54 const control = form.get(field)
34 55
35 if (control.dirty) this.formChanged = true 56 if (control.dirty) this.formChanged = true
36 57
37 // Don't care if dirty on force check 58 // Don't care if dirty on force check
38 const isDirty = control.dirty || forceCheck === true 59 const isDirty = control.dirty || forceCheck === true
39 if (control && isDirty && !control.valid) { 60 if (control && isDirty && !control.valid) {
40 const messages = this.validationMessages[ field ] 61 const messages = validationMessages[ field ]
41 for (const key in control.errors) { 62 for (const key in control.errors) {
42 this.formErrors[ field ] += messages[ key ] + ' ' 63 formErrors[ field ] += messages[ key ] + ' '
43 } 64 }
44 } 65 }
45 } 66 }
46 } 67 }
47 68
48 protected forceCheck () {
49 return this.onValueChanged(true)
50 }
51} 69}
diff --git a/client/src/app/shared/forms/form-validators/form-validator.service.ts b/client/src/app/shared/forms/form-validators/form-validator.service.ts
index 19a8bef25..249fdf119 100644
--- a/client/src/app/shared/forms/form-validators/form-validator.service.ts
+++ b/client/src/app/shared/forms/form-validators/form-validator.service.ts
@@ -7,10 +7,10 @@ export type BuildFormValidator = {
7 MESSAGES: { [ name: string ]: string } 7 MESSAGES: { [ name: string ]: string }
8} 8}
9export type BuildFormArgument = { 9export type BuildFormArgument = {
10 [ id: string ]: BuildFormValidator 10 [ id: string ]: BuildFormValidator | BuildFormArgument
11} 11}
12export type BuildFormDefaultValues = { 12export type BuildFormDefaultValues = {
13 [ name: string ]: string | string[] 13 [ name: string ]: string | string[] | BuildFormDefaultValues
14} 14}
15 15
16@Injectable() 16@Injectable()
@@ -29,7 +29,16 @@ export class FormValidatorService {
29 formErrors[name] = '' 29 formErrors[name] = ''
30 30
31 const field = obj[name] 31 const field = obj[name]
32 if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES 32 if (this.isRecursiveField(field)) {
33 const result = this.buildForm(field as BuildFormArgument, defaultValues[name] as BuildFormDefaultValues)
34 group[name] = result.form
35 formErrors[name] = result.formErrors
36 validationMessages[name] = result.validationMessages
37
38 continue
39 }
40
41 if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string }
33 42
34 const defaultValue = defaultValues[name] || '' 43 const defaultValue = defaultValues[name] || ''
35 44
@@ -52,13 +61,27 @@ export class FormValidatorService {
52 formErrors[name] = '' 61 formErrors[name] = ''
53 62
54 const field = obj[name] 63 const field = obj[name]
55 if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES 64 if (this.isRecursiveField(field)) {
65 this.updateForm(
66 form[name],
67 formErrors[name] as FormReactiveErrors,
68 validationMessages[name] as FormReactiveValidationMessages,
69 obj[name] as BuildFormArgument,
70 defaultValues[name] as BuildFormDefaultValues
71 )
72 continue
73 }
74
75 if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string }
56 76
57 const defaultValue = defaultValues[name] || '' 77 const defaultValue = defaultValues[name] || ''
58 78
59 if (field && field.VALIDATORS) form.addControl(name, new FormControl(defaultValue, field.VALIDATORS)) 79 if (field && field.VALIDATORS) form.addControl(name, new FormControl(defaultValue, field.VALIDATORS as ValidatorFn[]))
60 else form.addControl(name, new FormControl(defaultValue)) 80 else form.addControl(name, new FormControl(defaultValue))
61 } 81 }
62 } 82 }
63 83
84 private isRecursiveField (field: any) {
85 return field && typeof field === 'object' && !field.MESSAGES && !field.VALIDATORS
86 }
64} 87}
diff --git a/client/src/app/shared/forms/form-validators/index.ts b/client/src/app/shared/forms/form-validators/index.ts
index 74e385b3d..fdcbedb71 100644
--- a/client/src/app/shared/forms/form-validators/index.ts
+++ b/client/src/app/shared/forms/form-validators/index.ts
@@ -1,6 +1,7 @@
1export * from './custom-config-validators.service' 1export * from './custom-config-validators.service'
2export * from './form-validator.service' 2export * from './form-validator.service'
3export * from './host' 3export * from './host'
4export * from './instance-validators.service'
4export * from './login-validators.service' 5export * from './login-validators.service'
5export * from './reset-password-validators.service' 6export * from './reset-password-validators.service'
6export * from './user-validators.service' 7export * from './user-validators.service'
diff --git a/client/src/app/shared/forms/form-validators/instance-validators.service.ts b/client/src/app/shared/forms/form-validators/instance-validators.service.ts
new file mode 100644
index 000000000..5bb852858
--- /dev/null
+++ b/client/src/app/shared/forms/form-validators/instance-validators.service.ts
@@ -0,0 +1,48 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Validators } from '@angular/forms'
3import { BuildFormValidator } from '@app/shared'
4import { Injectable } from '@angular/core'
5
6@Injectable()
7export class InstanceValidatorsService {
8 readonly FROM_EMAIL: BuildFormValidator
9 readonly FROM_NAME: BuildFormValidator
10 readonly BODY: BuildFormValidator
11
12 constructor (private i18n: I18n) {
13
14 this.FROM_EMAIL = {
15 VALIDATORS: [ Validators.required, Validators.email ],
16 MESSAGES: {
17 'required': this.i18n('Email is required.'),
18 'email': this.i18n('Email must be valid.')
19 }
20 }
21
22 this.FROM_NAME = {
23 VALIDATORS: [
24 Validators.required,
25 Validators.minLength(1),
26 Validators.maxLength(120)
27 ],
28 MESSAGES: {
29 'required': this.i18n('Your name is required.'),
30 'minlength': this.i18n('Your name must be at least 1 character long.'),
31 'maxlength': this.i18n('Your name cannot be more than 120 characters long.')
32 }
33 }
34
35 this.BODY = {
36 VALIDATORS: [
37 Validators.required,
38 Validators.minLength(3),
39 Validators.maxLength(5000)
40 ],
41 MESSAGES: {
42 'required': this.i18n('A message is required.'),
43 'minlength': this.i18n('The message must be at least 3 characters long.'),
44 'maxlength': this.i18n('The message cannot be more than 5000 characters long.')
45 }
46 }
47 }
48}
diff --git a/client/src/app/shared/forms/form-validators/user-validators.service.ts b/client/src/app/shared/forms/form-validators/user-validators.service.ts
index 1fd1cdf68..6589b2580 100644
--- a/client/src/app/shared/forms/form-validators/user-validators.service.ts
+++ b/client/src/app/shared/forms/form-validators/user-validators.service.ts
@@ -23,15 +23,15 @@ export class UserValidatorsService {
23 this.USER_USERNAME = { 23 this.USER_USERNAME = {
24 VALIDATORS: [ 24 VALIDATORS: [
25 Validators.required, 25 Validators.required,
26 Validators.minLength(3), 26 Validators.minLength(1),
27 Validators.maxLength(20), 27 Validators.maxLength(50),
28 Validators.pattern(/^[a-z0-9._]+$/) 28 Validators.pattern(/^[a-z0-9][a-z0-9._]*$/)
29 ], 29 ],
30 MESSAGES: { 30 MESSAGES: {
31 'required': this.i18n('Username is required.'), 31 'required': this.i18n('Username is required.'),
32 'minlength': this.i18n('Username must be at least 3 characters long.'), 32 'minlength': this.i18n('Username must be at least 1 character long.'),
33 'maxlength': this.i18n('Username cannot be more than 20 characters long.'), 33 'maxlength': this.i18n('Username cannot be more than 50 characters long.'),
34 'pattern': this.i18n('Username should be only lowercase alphanumeric characters.') 34 'pattern': this.i18n('Username should be lowercase alphanumeric; dots and underscores are allowed.')
35 } 35 }
36 } 36 }
37 37
@@ -88,24 +88,24 @@ export class UserValidatorsService {
88 this.USER_DISPLAY_NAME = { 88 this.USER_DISPLAY_NAME = {
89 VALIDATORS: [ 89 VALIDATORS: [
90 Validators.required, 90 Validators.required,
91 Validators.minLength(3), 91 Validators.minLength(1),
92 Validators.maxLength(120) 92 Validators.maxLength(50)
93 ], 93 ],
94 MESSAGES: { 94 MESSAGES: {
95 'required': this.i18n('Display name is required.'), 95 'required': this.i18n('Display name is required.'),
96 'minlength': this.i18n('Display name must be at least 3 characters long.'), 96 'minlength': this.i18n('Display name must be at least 1 character long.'),
97 'maxlength': this.i18n('Display name cannot be more than 120 characters long.') 97 'maxlength': this.i18n('Display name cannot be more than 50 characters long.')
98 } 98 }
99 } 99 }
100 100
101 this.USER_DESCRIPTION = { 101 this.USER_DESCRIPTION = {
102 VALIDATORS: [ 102 VALIDATORS: [
103 Validators.minLength(3), 103 Validators.minLength(3),
104 Validators.maxLength(250) 104 Validators.maxLength(1000)
105 ], 105 ],
106 MESSAGES: { 106 MESSAGES: {
107 'minlength': this.i18n('Description must be at least 3 characters long.'), 107 'minlength': this.i18n('Description must be at least 3 characters long.'),
108 'maxlength': this.i18n('Description cannot be more than 250 characters long.') 108 'maxlength': this.i18n('Description cannot be more than 1000 characters long.')
109 } 109 }
110 } 110 }
111 111
diff --git a/client/src/app/shared/forms/form-validators/video-abuse-validators.service.ts b/client/src/app/shared/forms/form-validators/video-abuse-validators.service.ts
index 6e9806611..fcc966b84 100644
--- a/client/src/app/shared/forms/form-validators/video-abuse-validators.service.ts
+++ b/client/src/app/shared/forms/form-validators/video-abuse-validators.service.ts
@@ -10,20 +10,20 @@ export class VideoAbuseValidatorsService {
10 10
11 constructor (private i18n: I18n) { 11 constructor (private i18n: I18n) {
12 this.VIDEO_ABUSE_REASON = { 12 this.VIDEO_ABUSE_REASON = {
13 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(300) ], 13 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ],
14 MESSAGES: { 14 MESSAGES: {
15 'required': this.i18n('Report reason is required.'), 15 'required': this.i18n('Report reason is required.'),
16 'minlength': this.i18n('Report reason must be at least 2 characters long.'), 16 'minlength': this.i18n('Report reason must be at least 2 characters long.'),
17 'maxlength': this.i18n('Report reason cannot be more than 300 characters long.') 17 'maxlength': this.i18n('Report reason cannot be more than 3000 characters long.')
18 } 18 }
19 } 19 }
20 20
21 this.VIDEO_ABUSE_MODERATION_COMMENT = { 21 this.VIDEO_ABUSE_MODERATION_COMMENT = {
22 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(300) ], 22 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ],
23 MESSAGES: { 23 MESSAGES: {
24 'required': this.i18n('Moderation comment is required.'), 24 'required': this.i18n('Moderation comment is required.'),
25 'minlength': this.i18n('Moderation comment must be at least 2 characters long.'), 25 'minlength': this.i18n('Moderation comment must be at least 2 characters long.'),
26 'maxlength': this.i18n('Moderation comment cannot be more than 300 characters long.') 26 'maxlength': this.i18n('Moderation comment cannot be more than 3000 characters long.')
27 } 27 }
28 } 28 }
29 } 29 }
diff --git a/client/src/app/shared/forms/form-validators/video-change-ownership-validators.service.ts b/client/src/app/shared/forms/form-validators/video-change-ownership-validators.service.ts
index 087b80b44..c6fbb7538 100644
--- a/client/src/app/shared/forms/form-validators/video-change-ownership-validators.service.ts
+++ b/client/src/app/shared/forms/form-validators/video-change-ownership-validators.service.ts
@@ -1,5 +1,5 @@
1import { I18n } from '@ngx-translate/i18n-polyfill' 1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Validators } from '@angular/forms' 2import { AbstractControl, ValidationErrors, Validators } from '@angular/forms'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { BuildFormValidator } from '@app/shared' 4import { BuildFormValidator } from '@app/shared'
5 5
@@ -9,10 +9,19 @@ export class VideoChangeOwnershipValidatorsService {
9 9
10 constructor (private i18n: I18n) { 10 constructor (private i18n: I18n) {
11 this.USERNAME = { 11 this.USERNAME = {
12 VALIDATORS: [ Validators.required ], 12 VALIDATORS: [ Validators.required, this.localAccountValidator ],
13 MESSAGES: { 13 MESSAGES: {
14 'required': this.i18n('The username is required.') 14 'required': this.i18n('The username is required.'),
15 'localAccountOnly': this.i18n('You can only transfer ownership to a local account')
15 } 16 }
16 } 17 }
17 } 18 }
19
20 localAccountValidator (control: AbstractControl): ValidationErrors {
21 if (control.value.includes('@')) {
22 return { 'localAccountOnly': true }
23 }
24
25 return null
26 }
18} 27}
diff --git a/client/src/app/shared/forms/form-validators/video-channel-validators.service.ts b/client/src/app/shared/forms/form-validators/video-channel-validators.service.ts
index 1ce3a0dca..1c519c10a 100644
--- a/client/src/app/shared/forms/form-validators/video-channel-validators.service.ts
+++ b/client/src/app/shared/forms/form-validators/video-channel-validators.service.ts
@@ -14,50 +14,50 @@ export class VideoChannelValidatorsService {
14 this.VIDEO_CHANNEL_NAME = { 14 this.VIDEO_CHANNEL_NAME = {
15 VALIDATORS: [ 15 VALIDATORS: [
16 Validators.required, 16 Validators.required,
17 Validators.minLength(3), 17 Validators.minLength(1),
18 Validators.maxLength(20), 18 Validators.maxLength(50),
19 Validators.pattern(/^[a-z0-9._]+$/) 19 Validators.pattern(/^[a-z0-9][a-z0-9._]*$/)
20 ], 20 ],
21 MESSAGES: { 21 MESSAGES: {
22 'required': this.i18n('Name is required.'), 22 'required': this.i18n('Name is required.'),
23 'minlength': this.i18n('Name must be at least 3 characters long.'), 23 'minlength': this.i18n('Name must be at least 1 character long.'),
24 'maxlength': this.i18n('Name cannot be more than 20 characters long.'), 24 'maxlength': this.i18n('Name cannot be more than 50 characters long.'),
25 'pattern': this.i18n('Name should be only lowercase alphanumeric characters.') 25 'pattern': this.i18n('Name should be lowercase alphanumeric; dots and underscores are allowed.')
26 } 26 }
27 } 27 }
28 28
29 this.VIDEO_CHANNEL_DISPLAY_NAME = { 29 this.VIDEO_CHANNEL_DISPLAY_NAME = {
30 VALIDATORS: [ 30 VALIDATORS: [
31 Validators.required, 31 Validators.required,
32 Validators.minLength(3), 32 Validators.minLength(1),
33 Validators.maxLength(120) 33 Validators.maxLength(50)
34 ], 34 ],
35 MESSAGES: { 35 MESSAGES: {
36 'required': i18n('Display name is required.'), 36 'required': i18n('Display name is required.'),
37 'minlength': i18n('Display name must be at least 3 characters long.'), 37 'minlength': i18n('Display name must be at least 1 character long.'),
38 'maxlength': i18n('Display name cannot be more than 120 characters long.') 38 'maxlength': i18n('Display name cannot be more than 50 characters long.')
39 } 39 }
40 } 40 }
41 41
42 this.VIDEO_CHANNEL_DESCRIPTION = { 42 this.VIDEO_CHANNEL_DESCRIPTION = {
43 VALIDATORS: [ 43 VALIDATORS: [
44 Validators.minLength(3), 44 Validators.minLength(3),
45 Validators.maxLength(500) 45 Validators.maxLength(1000)
46 ], 46 ],
47 MESSAGES: { 47 MESSAGES: {
48 'minlength': i18n('Description must be at least 3 characters long.'), 48 'minlength': i18n('Description must be at least 3 characters long.'),
49 'maxlength': i18n('Description cannot be more than 500 characters long.') 49 'maxlength': i18n('Description cannot be more than 1000 characters long.')
50 } 50 }
51 } 51 }
52 52
53 this.VIDEO_CHANNEL_SUPPORT = { 53 this.VIDEO_CHANNEL_SUPPORT = {
54 VALIDATORS: [ 54 VALIDATORS: [
55 Validators.minLength(3), 55 Validators.minLength(3),
56 Validators.maxLength(500) 56 Validators.maxLength(1000)
57 ], 57 ],
58 MESSAGES: { 58 MESSAGES: {
59 'minlength': i18n('Support text must be at least 3 characters long.'), 59 'minlength': i18n('Support text must be at least 3 characters long.'),
60 'maxlength': i18n('Support text cannot be more than 500 characters long.') 60 'maxlength': i18n('Support text cannot be more than 1000 characters long.')
61 } 61 }
62 } 62 }
63 } 63 }
diff --git a/client/src/app/shared/forms/form-validators/video-validators.service.ts b/client/src/app/shared/forms/form-validators/video-validators.service.ts
index 396be6f3b..81ed0666f 100644
--- a/client/src/app/shared/forms/form-validators/video-validators.service.ts
+++ b/client/src/app/shared/forms/form-validators/video-validators.service.ts
@@ -79,10 +79,10 @@ export class VideoValidatorsService {
79 } 79 }
80 80
81 this.VIDEO_SUPPORT = { 81 this.VIDEO_SUPPORT = {
82 VALIDATORS: [ Validators.minLength(3), Validators.maxLength(500) ], 82 VALIDATORS: [ Validators.minLength(3), Validators.maxLength(1000) ],
83 MESSAGES: { 83 MESSAGES: {
84 'minlength': this.i18n('Video support must be at least 3 characters long.'), 84 'minlength': this.i18n('Video support must be at least 3 characters long.'),
85 'maxlength': this.i18n('Video support cannot be more than 500 characters long.') 85 'maxlength': this.i18n('Video support cannot be more than 1000 characters long.')
86 } 86 }
87 } 87 }
88 88
diff --git a/client/src/app/shared/forms/index.ts b/client/src/app/shared/forms/index.ts
index 41c321c4c..8febbfee9 100644
--- a/client/src/app/shared/forms/index.ts
+++ b/client/src/app/shared/forms/index.ts
@@ -1,3 +1,4 @@
1export * from './form-validators' 1export * from './form-validators'
2export * from './form-reactive' 2export * from './form-reactive'
3export * from './reactive-file.component' 3export * from './reactive-file.component'
4export * from './textarea-autoresize.directive'
diff --git a/client/src/app/shared/forms/markdown-textarea.component.ts b/client/src/app/shared/forms/markdown-textarea.component.ts
index b99169ed2..e87aca0d4 100644
--- a/client/src/app/shared/forms/markdown-textarea.component.ts
+++ b/client/src/app/shared/forms/markdown-textarea.component.ts
@@ -1,10 +1,10 @@
1import { debounceTime, distinctUntilChanged } from 'rxjs/operators' 1import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
2import { Component, forwardRef, Input, OnInit } from '@angular/core' 2import { Component, forwardRef, Input, OnInit } from '@angular/core'
3import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 3import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
4import { MarkdownService } from '@app/videos/shared'
5import { Subject } from 'rxjs' 4import { Subject } from 'rxjs'
6import truncate from 'lodash-es/truncate' 5import truncate from 'lodash-es/truncate'
7import { ScreenService } from '@app/shared/misc/screen.service' 6import { ScreenService } from '@app/shared/misc/screen.service'
7import { MarkdownService } from '@app/shared/renderer'
8 8
9@Component({ 9@Component({
10 selector: 'my-markdown-textarea', 10 selector: 'my-markdown-textarea',
diff --git a/client/src/app/shared/forms/peertube-checkbox.component.html b/client/src/app/shared/forms/peertube-checkbox.component.html
index 38691f050..7b8bcf601 100644
--- a/client/src/app/shared/forms/peertube-checkbox.component.html
+++ b/client/src/app/shared/forms/peertube-checkbox.component.html
@@ -1,10 +1,10 @@
1<div class="form-group"> 1<div class="root">
2 <label class="form-group-checkbox"> 2 <label class="form-group-checkbox">
3 <input type="checkbox" [(ngModel)]="checked" (ngModelChange)="onModelChange()" [id]="inputName" [disabled]="isDisabled" /> 3 <input type="checkbox" [(ngModel)]="checked" (ngModelChange)="onModelChange()" [id]="inputName" [disabled]="disabled" />
4 <span role="checkbox" [attr.aria-checked]="checked"></span> 4 <span role="checkbox" [attr.aria-checked]="checked"></span>
5 <span *ngIf="labelText">{{ labelText }}</span> 5 <span *ngIf="labelText">{{ labelText }}</span>
6 <span *ngIf="labelHtml" [innerHTML]="labelHtml"></span> 6 <span *ngIf="labelHtml" [innerHTML]="labelHtml"></span>
7 </label> 7 </label>
8 8
9 <my-help *ngIf="helpHtml" tooltipPlacement="top" helpType="custom" i18n-customHtml [customHtml]="helpHtml"></my-help> 9 <my-help *ngIf="helpHtml" tooltipPlacement="top" helpType="custom" i18n-customHtml [customHtml]="helpHtml"></my-help>
10</div> \ No newline at end of file 10</div>
diff --git a/client/src/app/shared/forms/peertube-checkbox.component.scss b/client/src/app/shared/forms/peertube-checkbox.component.scss
index ee133f190..6e4e20775 100644
--- a/client/src/app/shared/forms/peertube-checkbox.component.scss
+++ b/client/src/app/shared/forms/peertube-checkbox.component.scss
@@ -1,7 +1,7 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4.form-group { 4.root {
5 display: flex; 5 display: flex;
6 6
7 .form-group-checkbox { 7 .form-group-checkbox {
@@ -20,6 +20,10 @@
20 } 20 }
21 } 21 }
22 22
23 label {
24 margin-bottom: 0;
25 }
26
23 my-help { 27 my-help {
24 position: relative; 28 position: relative;
25 top: -2px; 29 top: -2px;
diff --git a/client/src/app/shared/forms/peertube-checkbox.component.ts b/client/src/app/shared/forms/peertube-checkbox.component.ts
index bbc9904df..c1a6915e8 100644
--- a/client/src/app/shared/forms/peertube-checkbox.component.ts
+++ b/client/src/app/shared/forms/peertube-checkbox.component.ts
@@ -19,8 +19,7 @@ export class PeertubeCheckboxComponent implements ControlValueAccessor {
19 @Input() labelText: string 19 @Input() labelText: string
20 @Input() labelHtml: string 20 @Input() labelHtml: string
21 @Input() helpHtml: string 21 @Input() helpHtml: string
22 22 @Input() disabled = false
23 isDisabled = false
24 23
25 propagateChange = (_: any) => { /* empty */ } 24 propagateChange = (_: any) => { /* empty */ }
26 25
@@ -41,6 +40,6 @@ export class PeertubeCheckboxComponent implements ControlValueAccessor {
41 } 40 }
42 41
43 setDisabledState (isDisabled: boolean) { 42 setDisabledState (isDisabled: boolean) {
44 this.isDisabled = isDisabled 43 this.disabled = isDisabled
45 } 44 }
46} 45}
diff --git a/client/src/app/shared/forms/reactive-file.component.ts b/client/src/app/shared/forms/reactive-file.component.ts
index 8d22aa56c..f60c38e8d 100644
--- a/client/src/app/shared/forms/reactive-file.component.ts
+++ b/client/src/app/shared/forms/reactive-file.component.ts
@@ -1,6 +1,6 @@
1import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core' 1import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'
2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
3import { NotificationsService } from 'angular2-notifications' 3import { Notifier } from '@app/core'
4import { I18n } from '@ngx-translate/i18n-polyfill' 4import { I18n } from '@ngx-translate/i18n-polyfill'
5 5
6@Component({ 6@Component({
@@ -30,7 +30,7 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor {
30 private file: File 30 private file: File
31 31
32 constructor ( 32 constructor (
33 private notificationsService: NotificationsService, 33 private notifier: Notifier,
34 private i18n: I18n 34 private i18n: I18n
35 ) {} 35 ) {}
36 36
@@ -49,7 +49,18 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor {
49 const [ file ] = event.target.files 49 const [ file ] = event.target.files
50 50
51 if (file.size > this.maxFileSize) { 51 if (file.size > this.maxFileSize) {
52 this.notificationsService.error(this.i18n('Error'), this.i18n('This file is too large.')) 52 this.notifier.error(this.i18n('This file is too large.'))
53 return
54 }
55
56 const extension = '.' + file.name.split('.').pop()
57 if (this.extensions.includes(extension) === false) {
58 const message = this.i18n(
59 'PeerTube cannot handle this kind of file. Accepted extensions are {{extensions}}.',
60 { extensions: this.allowedExtensionsMessage }
61 )
62 this.notifier.error(message)
63
53 return 64 return
54 } 65 }
55 66
diff --git a/client/src/app/shared/forms/textarea-autoresize.directive.ts b/client/src/app/shared/forms/textarea-autoresize.directive.ts
new file mode 100644
index 000000000..f8c855c16
--- /dev/null
+++ b/client/src/app/shared/forms/textarea-autoresize.directive.ts
@@ -0,0 +1,25 @@
1// Thanks: https://github.com/evseevdev/ngx-textarea-autosize
2import { AfterViewInit, Directive, ElementRef, HostBinding, HostListener } from '@angular/core'
3
4@Directive({
5 selector: 'textarea[myAutoResize]'
6})
7export class TextareaAutoResizeDirective implements AfterViewInit {
8 @HostBinding('attr.rows') rows = '1'
9 @HostBinding('style.overflow') overflow = 'hidden'
10
11 constructor (private elem: ElementRef) { }
12
13 public ngAfterViewInit () {
14 this.resize()
15 }
16
17 @HostListener('input')
18 resize () {
19 const textarea = this.elem.nativeElement as HTMLTextAreaElement
20 // Reset textarea height to auto that correctly calculate the new height
21 textarea.style.height = 'auto'
22 // Set new height
23 textarea.style.height = `${textarea.scrollHeight}px`
24 }
25}