aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+signup/+register/steps
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/+signup/+register/steps')
-rw-r--r--client/src/app/+signup/+register/steps/index.ts4
-rw-r--r--client/src/app/+signup/+register/steps/register-step-about.component.html39
-rw-r--r--client/src/app/+signup/+register/steps/register-step-about.component.scss53
-rw-r--r--client/src/app/+signup/+register/steps/register-step-about.component.ts19
-rw-r--r--client/src/app/+signup/+register/steps/register-step-channel.component.html55
-rw-r--r--client/src/app/+signup/+register/steps/register-step-channel.component.ts57
-rw-r--r--client/src/app/+signup/+register/steps/register-step-terms.component.html16
-rw-r--r--client/src/app/+signup/+register/steps/register-step-terms.component.ts48
-rw-r--r--client/src/app/+signup/+register/steps/register-step-user.component.html73
-rw-r--r--client/src/app/+signup/+register/steps/register-step-user.component.ts63
-rw-r--r--client/src/app/+signup/+register/steps/step.component.scss27
11 files changed, 454 insertions, 0 deletions
diff --git a/client/src/app/+signup/+register/steps/index.ts b/client/src/app/+signup/+register/steps/index.ts
new file mode 100644
index 000000000..b5eae7468
--- /dev/null
+++ b/client/src/app/+signup/+register/steps/index.ts
@@ -0,0 +1,4 @@
1export * from './register-step-about.component'
2export * from './register-step-channel.component'
3export * from './register-step-terms.component'
4export * from './register-step-user.component'
diff --git a/client/src/app/+signup/+register/steps/register-step-about.component.html b/client/src/app/+signup/+register/steps/register-step-about.component.html
new file mode 100644
index 000000000..f93de8ce9
--- /dev/null
+++ b/client/src/app/+signup/+register/steps/register-step-about.component.html
@@ -0,0 +1,39 @@
1<div class="why">
2 <h3 i18n>Why creating an account?</h3>
3
4 <p i18n>
5 As you probably noticed: creating an account is not necessary to watch video son {{ instanceName }}.
6 <br />
7 However, creating an account on {{ instanceName }} will allow you to:
8 </p>
9
10 <ul>
11 <li i18n><strong>Comment</strong> videos</li>
12 <li i18n><strong>Subscribe</strong> to channels to be notified of new videos</li>
13 <li i18n>Have access to your <strong>watch history</strong></li>
14 <li *ngIf="!videoUploadDisabled" i18n>Create your channel to <strong>publish videos</strong></li>
15 </ul>
16</div>
17
18<div>
19 <h4 i18n>You're using Mastodon, ActivityPub or a RSS feed aggregator?</h4>
20
21 <p i18n>
22 You can already follow {{ instanceName }} using your favorite tool.
23 </p>
24</div>
25
26<div class="callout callout-orange callout-light">
27 <div class="mascot-container" style="min-width: 140px">
28 <img class="mascot" width="140px" height="160px" src="/client/assets/images/mascot/happy.svg" alt="mascot"/>
29 </div>
30
31 <div class="callout-content">
32 <h4 i18>This website is a GAFAM alternative</h4>
33
34 <p i18n>
35 {{ instanceName }} has been created using <a class="link-orange" target="_blank" rel="noopener noreferrer" href="https://joinpeertube.org">PeerTube</a>, a video creation platform developed by Framasoft.
36 <a class="link-orange" target="_blank" rel="noopener noreferrer" href="https://framasoft.org">Framasoft</a> is a french non-profit organization that offers alternatives to Big Tech's digital tools
37 </p>
38 </div>
39</div>
diff --git a/client/src/app/+signup/+register/steps/register-step-about.component.scss b/client/src/app/+signup/+register/steps/register-step-about.component.scss
new file mode 100644
index 000000000..ab6d6dd4d
--- /dev/null
+++ b/client/src/app/+signup/+register/steps/register-step-about.component.scss
@@ -0,0 +1,53 @@
1@use '_variables' as *;
2@use '_mixins' as *;
3
4h3 {
5 font-weight: $font-bold;
6 font-size: 24px;
7}
8
9h4 {
10 font-size: 18px;
11 font-weight: $font-bold;
12}
13
14.why {
15 margin-bottom: 30px;
16}
17
18.callout {
19 margin: 75px auto 25px;
20 border-width: 2px;
21 display: flex;
22
23 .mascot-container {
24 position: relative;
25
26 .mascot {
27 position: absolute;
28 top: -65px;
29 }
30 }
31
32 .callout-content {
33 margin-left: 30px;
34
35 p {
36 margin: 0;
37 }
38 }
39}
40
41@media screen and (max-width: $small-view) {
42 .callout {
43 margin-top: 20px;
44
45 .mascot-container {
46 display: none;
47 }
48
49 .callout-content {
50 margin-left: 0;
51 }
52 }
53}
diff --git a/client/src/app/+signup/+register/steps/register-step-about.component.ts b/client/src/app/+signup/+register/steps/register-step-about.component.ts
new file mode 100644
index 000000000..9a0941016
--- /dev/null
+++ b/client/src/app/+signup/+register/steps/register-step-about.component.ts
@@ -0,0 +1,19 @@
1import { Component, Input } from '@angular/core'
2import { ServerService } from '@app/core'
3
4@Component({
5 selector: 'my-register-step-about',
6 templateUrl: './register-step-about.component.html',
7 styleUrls: [ './register-step-about.component.scss' ]
8})
9export class RegisterStepAboutComponent {
10 @Input() videoUploadDisabled: boolean
11
12 constructor (private serverService: ServerService) {
13
14 }
15
16 get instanceName () {
17 return this.serverService.getHTMLConfig().instance.name
18 }
19}
diff --git a/client/src/app/+signup/+register/steps/register-step-channel.component.html b/client/src/app/+signup/+register/steps/register-step-channel.component.html
new file mode 100644
index 000000000..c79256c68
--- /dev/null
+++ b/client/src/app/+signup/+register/steps/register-step-channel.component.html
@@ -0,0 +1,55 @@
1<div class="mb-5">
2 <p i18n>
3 You want to <strong>publish videos</strong> on {{ instanceName }}? Then you need to create your first <strong>channel</strong>.
4 </p>
5
6 <p i18n>
7 You might want to <strong>create a channel by theme:</strong> for example, you can create a channel named "SweetMelodies"
8 to publish your piano concerts and another one "Ecology" in which you publish your videos talking about ecology.
9 </p>
10
11 <p i18n *ngIf="videoQuota !== -1">
12 {{ instanceName }} administrators allow you to publish up to <strong>{{ videoQuota | bytes: 0 }} of videos</strong> on their website.
13 </p>
14</div>
15
16<form role="form" [formGroup]="form">
17
18 <div class="row">
19
20 <div class="col-md-12 col-xl-6 form-group">
21 <label for="displayName" i18n>Channel display name</label>
22
23 <div i18n class="form-group-description">This is the name that will be publicly visible by other users.</div>
24
25 <div class="input-group">
26 <input
27 type="text" id="displayName" i18n-placeholder placeholder="Example: Sweet Melodies"
28 formControlName="displayName" [ngClass]="{ 'input-error': formErrors['displayName'] }"
29 >
30 </div>
31
32 <div *ngIf="formErrors.displayName" class="form-error">{{ formErrors.displayName }}</div>
33 </div>
34
35 <div class="col-md-12 col-xl-6 form-group">
36 <label for="name" i18n>Channel identifier</label>
37
38 <div i18n class="form-group-description">This is the name that will be displayed in your profile URL.</div>
39
40 <div class="input-group">
41 <input
42 type="text" id="name" i18n-placeholder placeholder="Example: sweetmelodies24"
43 formControlName="name" [ngClass]="{ 'input-error': formErrors['name'] }"
44 >
45 <div class="input-group-text">@{{ instanceHost }}</div>
46 </div>
47
48 <div *ngIf="formErrors.name" class="form-error">{{ formErrors.name }}</div>
49
50 <div *ngIf="isSameThanUsername()" class="form-error" i18n>
51 Channel identifier cannot be the same as your account name. You can click on the first step to update your account name.
52 </div>
53 </div>
54 </div>
55</form>
diff --git a/client/src/app/+signup/+register/steps/register-step-channel.component.ts b/client/src/app/+signup/+register/steps/register-step-channel.component.ts
new file mode 100644
index 000000000..c10b568ba
--- /dev/null
+++ b/client/src/app/+signup/+register/steps/register-step-channel.component.ts
@@ -0,0 +1,57 @@
1import { concat, of } from 'rxjs'
2import { pairwise } from 'rxjs/operators'
3import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
4import { FormGroup } from '@angular/forms'
5import { VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR, VIDEO_CHANNEL_NAME_VALIDATOR } from '@app/shared/form-validators/video-channel-validators'
6import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
7import { UserSignupService } from '@app/shared/shared-users'
8
9@Component({
10 selector: 'my-register-step-channel',
11 templateUrl: './register-step-channel.component.html',
12 styleUrls: [ './step.component.scss' ]
13})
14export class RegisterStepChannelComponent extends FormReactive implements OnInit {
15 @Input() username: string
16 @Input() instanceName: string
17 @Input() videoQuota: number
18
19 @Output() formBuilt = new EventEmitter<FormGroup>()
20
21 constructor (
22 protected formValidatorService: FormValidatorService,
23 private userSignupService: UserSignupService
24 ) {
25 super()
26 }
27
28 get instanceHost () {
29 return window.location.host
30 }
31
32 ngOnInit () {
33 this.buildForm({
34 displayName: VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR,
35 name: VIDEO_CHANNEL_NAME_VALIDATOR
36 })
37
38 setTimeout(() => this.formBuilt.emit(this.form))
39
40 concat(
41 of(''),
42 this.form.get('displayName').valueChanges
43 ).pipe(pairwise())
44 .subscribe(([ oldValue, newValue ]) => this.onDisplayNameChange(oldValue, newValue))
45 }
46
47 isSameThanUsername () {
48 return this.username && this.username === this.form.value['name']
49 }
50
51 private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) {
52 const name = this.form.value['name'] || ''
53
54 const newName = this.userSignupService.getNewUsername(oldDisplayName, newDisplayName, name)
55 this.form.patchValue({ name: newName })
56 }
57}
diff --git a/client/src/app/+signup/+register/steps/register-step-terms.component.html b/client/src/app/+signup/+register/steps/register-step-terms.component.html
new file mode 100644
index 000000000..f54ca77e2
--- /dev/null
+++ b/client/src/app/+signup/+register/steps/register-step-terms.component.html
@@ -0,0 +1,16 @@
1<form role="form" [formGroup]="form">
2 <div class="form-group">
3 <my-peertube-checkbox inputName="terms" formControlName="terms">
4 <ng-template ptTemplate="label">
5 <ng-container i18n>
6 I am at least {{ minimumAge }} years old and agree
7 to the <a class="link-orange" (click)="onTermsClick($event)" href='#'>Terms</a>
8 <ng-container *ngIf="hasCodeOfConduct"> and to the <a (click)="onCodeOfConductClick($event)" href='#'>Code of Conduct</a></ng-container>
9 of this instance
10 </ng-container>
11 </ng-template>
12 </my-peertube-checkbox>
13
14 <div *ngIf="formErrors.terms" class="form-error">{{ formErrors.terms }}</div>
15 </div>
16</form>
diff --git a/client/src/app/+signup/+register/steps/register-step-terms.component.ts b/client/src/app/+signup/+register/steps/register-step-terms.component.ts
new file mode 100644
index 000000000..87d16696e
--- /dev/null
+++ b/client/src/app/+signup/+register/steps/register-step-terms.component.ts
@@ -0,0 +1,48 @@
1import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
2import { FormGroup } from '@angular/forms'
3import {
4 USER_TERMS_VALIDATOR
5} from '@app/shared/form-validators/user-validators'
6import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
7
8@Component({
9 selector: 'my-register-step-terms',
10 templateUrl: './register-step-terms.component.html',
11 styleUrls: [ './step.component.scss' ]
12})
13export class RegisterStepTermsComponent extends FormReactive implements OnInit {
14 @Input() hasCodeOfConduct = false
15 @Input() minimumAge = 16
16
17 @Output() formBuilt = new EventEmitter<FormGroup>()
18 @Output() termsClick = new EventEmitter<void>()
19 @Output() codeOfConductClick = new EventEmitter<void>()
20
21 constructor (
22 protected formValidatorService: FormValidatorService
23 ) {
24 super()
25 }
26
27 get instanceHost () {
28 return window.location.host
29 }
30
31 ngOnInit () {
32 this.buildForm({
33 terms: USER_TERMS_VALIDATOR
34 })
35
36 setTimeout(() => this.formBuilt.emit(this.form))
37 }
38
39 onTermsClick (event: Event) {
40 event.preventDefault()
41 this.termsClick.emit()
42 }
43
44 onCodeOfConductClick (event: Event) {
45 event.preventDefault()
46 this.codeOfConductClick.emit()
47 }
48}
diff --git a/client/src/app/+signup/+register/steps/register-step-user.component.html b/client/src/app/+signup/+register/steps/register-step-user.component.html
new file mode 100644
index 000000000..bffcf0346
--- /dev/null
+++ b/client/src/app/+signup/+register/steps/register-step-user.component.html
@@ -0,0 +1,73 @@
1<div class="alert pt-alert-primary" i18n *ngIf="videoUploadDisabled">
2 Video uploads are disabled on this instance, hence your account won't be able to upload videos.
3</div>
4
5<form role="form" [formGroup]="form">
6 <div class="row">
7
8 <div class="col-md-12 col-xl-6 form-group">
9 <label for="displayName" i18n>Public name</label>
10
11 <div class="form-group-description" i18n>
12 This is the name that will be publicly visible by other users.
13 </div>
14
15 <div class="input-group">
16 <input
17 type="text" id="displayName" i18n-placeholder placeholder="Example: John Doe"
18 formControlName="displayName" [ngClass]="{ 'input-error': formErrors['displayName'] }"
19 >
20 </div>
21
22 <div *ngIf="formErrors.displayName" class="form-error">{{ formErrors.displayName }}</div>
23 </div>
24
25 <div class="col-md-12 col-xl-6 form-group">
26 <label for="username" i18n>Username</label>
27
28 <div class="form-group-description" i18n>
29 This is the name that will be displayed in your profile URL.
30 </div>
31
32 <div class="input-group">
33 <input
34 type="text" id="username" i18n-placeholder placeholder="Example: john_doe58"
35 formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }"
36 >
37 <span class="input-group-text">@{{ instanceHost }}</span>
38 </div>
39
40 <div *ngIf="formErrors.username" class="form-error">{{ formErrors.username }}</div>
41 </div>
42 </div>
43
44 <div class="row">
45 <div class="col-md-12 col-xl-6 form-group">
46 <label for="email" i18n>Email</label>
47
48 <div *ngIf="requiresEmailVerification" class="form-group-description" i18n>
49 This email address will be used to validate your account.
50 </div>
51
52 <input
53 type="text" id="email" i18n-placeholder placeholder="Example: john@example.com"
54 formControlName="email" class="form-control" [ngClass]="{ 'input-error': formErrors['email'] }"
55 >
56
57 <div *ngIf="formErrors.email" class="form-error">{{ formErrors.email }}</div>
58 </div>
59
60 <div class="col-md-12 col-xl-6 form-group">
61 <label for="password" i18n>Password</label>
62
63 <div class="form-group-description">{{ getMinPasswordLengthMessage() }}</div>
64
65 <my-input-text
66 formControlName="password" inputId="password"
67 [ngClass]="{ 'input-error': formErrors['password'] }" autocomplete="new-password"
68 ></my-input-text>
69
70 <div *ngIf="formErrors.password" class="form-error">{{ formErrors.password }}</div>
71 </div>
72 </div>
73</form>
diff --git a/client/src/app/+signup/+register/steps/register-step-user.component.ts b/client/src/app/+signup/+register/steps/register-step-user.component.ts
new file mode 100644
index 000000000..b89e38a28
--- /dev/null
+++ b/client/src/app/+signup/+register/steps/register-step-user.component.ts
@@ -0,0 +1,63 @@
1import { concat, of } from 'rxjs'
2import { pairwise } from 'rxjs/operators'
3import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
4import { FormGroup } from '@angular/forms'
5import {
6 USER_DISPLAY_NAME_REQUIRED_VALIDATOR,
7 USER_EMAIL_VALIDATOR,
8 USER_PASSWORD_VALIDATOR,
9 USER_USERNAME_VALIDATOR
10} from '@app/shared/form-validators/user-validators'
11import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
12import { UserSignupService } from '@app/shared/shared-users'
13
14@Component({
15 selector: 'my-register-step-user',
16 templateUrl: './register-step-user.component.html',
17 styleUrls: [ './step.component.scss' ]
18})
19export class RegisterStepUserComponent extends FormReactive implements OnInit {
20 @Input() videoUploadDisabled = false
21 @Input() requiresEmailVerification = false
22
23 @Output() formBuilt = new EventEmitter<FormGroup>()
24
25 constructor (
26 protected formValidatorService: FormValidatorService,
27 private userSignupService: UserSignupService
28 ) {
29 super()
30 }
31
32 get instanceHost () {
33 return window.location.host
34 }
35
36 ngOnInit () {
37 this.buildForm({
38 displayName: USER_DISPLAY_NAME_REQUIRED_VALIDATOR,
39 username: USER_USERNAME_VALIDATOR,
40 password: USER_PASSWORD_VALIDATOR,
41 email: USER_EMAIL_VALIDATOR
42 })
43
44 setTimeout(() => this.formBuilt.emit(this.form))
45
46 concat(
47 of(''),
48 this.form.get('displayName').valueChanges
49 ).pipe(pairwise())
50 .subscribe(([ oldValue, newValue ]) => this.onDisplayNameChange(oldValue, newValue))
51 }
52
53 getMinPasswordLengthMessage () {
54 return USER_PASSWORD_VALIDATOR.MESSAGES.minlength
55 }
56
57 private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) {
58 const username = this.form.value['username'] || ''
59
60 const newUsername = this.userSignupService.getNewUsername(oldDisplayName, newDisplayName, username)
61 this.form.patchValue({ username: newUsername })
62 }
63}
diff --git a/client/src/app/+signup/+register/steps/step.component.scss b/client/src/app/+signup/+register/steps/step.component.scss
new file mode 100644
index 000000000..35cfdae91
--- /dev/null
+++ b/client/src/app/+signup/+register/steps/step.component.scss
@@ -0,0 +1,27 @@
1@use '_variables' as *;
2@use '_mixins' as *;
3
4input:not([type=submit]) {
5 @include peertube-input-text(100%);
6 display: block;
7
8 &#username,
9 &#name {
10 width: auto !important;
11 flex-grow: 1;
12 }
13}
14
15input[type=submit],
16button {
17 @include peertube-button;
18}
19
20label {
21 font-size: 18px;
22 margin-bottom: 5px;
23}
24
25.row {
26 margin-bottom: 30px;
27}