aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+signup/+register
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/+signup/+register')
-rw-r--r--client/src/app/+signup/+register/custom-stepper.component.html25
-rw-r--r--client/src/app/+signup/+register/custom-stepper.component.scss66
-rw-r--r--client/src/app/+signup/+register/custom-stepper.component.ts19
-rw-r--r--client/src/app/+signup/+register/register-routing.module.ts28
-rw-r--r--client/src/app/+signup/+register/register-step-channel.component.html50
-rw-r--r--client/src/app/+signup/+register/register-step-channel.component.ts40
-rw-r--r--client/src/app/+signup/+register/register-step-user.component.html54
-rw-r--r--client/src/app/+signup/+register/register-step-user.component.ts37
-rw-r--r--client/src/app/+signup/+register/register.component.html41
-rw-r--r--client/src/app/+signup/+register/register.component.scss58
-rw-r--r--client/src/app/+signup/+register/register.component.ts89
-rw-r--r--client/src/app/+signup/+register/register.module.ts33
12 files changed, 540 insertions, 0 deletions
diff --git a/client/src/app/+signup/+register/custom-stepper.component.html b/client/src/app/+signup/+register/custom-stepper.component.html
new file mode 100644
index 000000000..bf507fc4f
--- /dev/null
+++ b/client/src/app/+signup/+register/custom-stepper.component.html
@@ -0,0 +1,25 @@
1<section class="container">
2 <header>
3 <ng-container *ngFor="let step of steps; let i = index; let isLast = last;">
4 <div
5 class="step-info" [ngClass]="{ active: selectedIndex === i, completed: isCompleted(step) }"
6 (click)="onClick(i)"
7 >
8 <div class="step-index">
9 <ng-container *ngIf="!isCompleted(step)">{{ i + 1 }}</ng-container>
10 <my-global-icon *ngIf="isCompleted(step)" iconName="tick"></my-global-icon>
11 </div>
12
13 <div class="step-label">{{ step.label }}</div>
14 </div>
15
16 <!-- Do no display if this is the last child -->
17 <div *ngIf="!isLast" class="connector"></div>
18 </ng-container>
19 </header>
20
21 <div [style.display]="selected ? 'block' : 'none'">
22 <ng-container [ngTemplateOutlet]="selected.content"></ng-container>
23 </div>
24
25</section>
diff --git a/client/src/app/+signup/+register/custom-stepper.component.scss b/client/src/app/+signup/+register/custom-stepper.component.scss
new file mode 100644
index 000000000..2371c8ae5
--- /dev/null
+++ b/client/src/app/+signup/+register/custom-stepper.component.scss
@@ -0,0 +1,66 @@
1@import '_variables';
2@import '_mixins';
3
4$grey-color: #9CA3AB;
5$index-block-height: 32px;
6
7header {
8 display: flex;
9 justify-content: space-between;
10 font-size: 15px;
11 margin-bottom: 30px;
12
13 .step-info {
14 color: $grey-color;
15 display: flex;
16 flex-direction: column;
17 align-items: center;
18 width: $index-block-height;
19
20 .step-index {
21 display: flex;
22 justify-content: center;
23 align-items: center;
24 width: $index-block-height;
25 height: $index-block-height;
26 border-radius: 100px;
27 border: 2px solid $grey-color;
28 margin-bottom: 10px;
29
30 my-global-icon {
31 @include apply-svg-color(var(--mainBackgroundColor));
32
33 width: 22px;
34 height: 22px;
35 }
36 }
37
38 .step-label {
39 width: max-content;
40 }
41
42 &.active,
43 &.completed {
44 .step-index {
45 border-color: var(--mainColor);
46 background-color: var(--mainColor);
47 color: var(--mainBackgroundColor);
48 }
49
50 .step-label {
51 color: var(--mainColor);
52 }
53 }
54
55 &.completed {
56 cursor: pointer;
57 }
58 }
59
60 .connector {
61 flex: auto;
62 margin: $index-block-height/2 10px 0 10px;
63 height: 2px;
64 background-color: $grey-color;
65 }
66}
diff --git a/client/src/app/+signup/+register/custom-stepper.component.ts b/client/src/app/+signup/+register/custom-stepper.component.ts
new file mode 100644
index 000000000..2ae40f3a9
--- /dev/null
+++ b/client/src/app/+signup/+register/custom-stepper.component.ts
@@ -0,0 +1,19 @@
1import { Component } from '@angular/core'
2import { CdkStep, CdkStepper } from '@angular/cdk/stepper'
3
4@Component({
5 selector: 'my-custom-stepper',
6 templateUrl: './custom-stepper.component.html',
7 styleUrls: [ './custom-stepper.component.scss' ],
8 providers: [ { provide: CdkStepper, useExisting: CustomStepperComponent } ]
9})
10export class CustomStepperComponent extends CdkStepper {
11
12 onClick (index: number): void {
13 this.selectedIndex = index
14 }
15
16 isCompleted (step: CdkStep) {
17 return step.stepControl && step.stepControl.dirty && step.stepControl.valid
18 }
19}
diff --git a/client/src/app/+signup/+register/register-routing.module.ts b/client/src/app/+signup/+register/register-routing.module.ts
new file mode 100644
index 000000000..e3a5001dc
--- /dev/null
+++ b/client/src/app/+signup/+register/register-routing.module.ts
@@ -0,0 +1,28 @@
1import { NgModule } from '@angular/core'
2import { RouterModule, Routes } from '@angular/router'
3import { MetaGuard } from '@ngx-meta/core'
4import { RegisterComponent } from './register.component'
5import { ServerConfigResolver } from '@app/core/routing/server-config-resolver.service'
6import { UnloggedGuard } from '@app/core/routing/unlogged-guard.service'
7
8const registerRoutes: Routes = [
9 {
10 path: '',
11 component: RegisterComponent,
12 canActivate: [ MetaGuard, UnloggedGuard ],
13 data: {
14 meta: {
15 title: 'Register'
16 }
17 },
18 resolve: {
19 serverConfigLoaded: ServerConfigResolver
20 }
21 }
22]
23
24@NgModule({
25 imports: [ RouterModule.forChild(registerRoutes) ],
26 exports: [ RouterModule ]
27})
28export class RegisterRoutingModule {}
diff --git a/client/src/app/+signup/+register/register-step-channel.component.html b/client/src/app/+signup/+register/register-step-channel.component.html
new file mode 100644
index 000000000..68ea4473a
--- /dev/null
+++ b/client/src/app/+signup/+register/register-step-channel.component.html
@@ -0,0 +1,50 @@
1<form role="form" [formGroup]="form">
2
3 <div class="channel-explanations">
4 <p i18n>
5 A channel is an entity in which you upload your videos. Creating several of them helps you to organize and separate your content.<br />
6 For example, you could decide to have a channel to publish your piano concerts, and another channel in which you publish your videos talking about ecology.
7 </p>
8
9 <p>
10 Other users can decide to subscribe any channel they want, to be notified when you publish a new video.
11 </p>
12 </div>
13
14 <div class="form-group">
15 <label for="name" i18n>Channel name</label>
16
17 <div class="input-group">
18 <input
19 type="text" id="name" i18n-placeholder placeholder="Example: my_super_channel"
20 formControlName="name" [ngClass]="{ 'input-error': formErrors['name'] }"
21 >
22 <div class="input-group-append">
23 <span class="input-group-text">@{{ instanceHost }}</span>
24 </div>
25 </div>
26
27 <div *ngIf="formErrors.name" class="form-error">
28 {{ formErrors.name }}
29 </div>
30
31 <div *ngIf="isSameThanUsername()" class="form-error" i18n>
32 Channel name cannot be the same than your account name. You can click on the first step to update your account name.
33 </div>
34 </div>
35
36 <div class="form-group">
37 <label for="displayName" i18n>Channel display name</label>
38
39 <div class="input-group">
40 <input
41 type="text" id="displayName"
42 formControlName="displayName" [ngClass]="{ 'input-error': formErrors['displayName'] }"
43 >
44 </div>
45
46 <div *ngIf="formErrors.displayName" class="form-error">
47 {{ formErrors.displayName }}
48 </div>
49 </div>
50</form>
diff --git a/client/src/app/+signup/+register/register-step-channel.component.ts b/client/src/app/+signup/+register/register-step-channel.component.ts
new file mode 100644
index 000000000..9e13f75b3
--- /dev/null
+++ b/client/src/app/+signup/+register/register-step-channel.component.ts
@@ -0,0 +1,40 @@
1import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
2import { AuthService } from '@app/core'
3import { FormReactive, VideoChannelValidatorsService } from '@app/shared'
4import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
5import { FormGroup } from '@angular/forms'
6
7@Component({
8 selector: 'my-register-step-channel',
9 templateUrl: './register-step-channel.component.html',
10 styleUrls: [ './register.component.scss' ]
11})
12export class RegisterStepChannelComponent extends FormReactive implements OnInit {
13 @Input() username: string
14 @Output() formBuilt = new EventEmitter<FormGroup>()
15
16 constructor (
17 protected formValidatorService: FormValidatorService,
18 private authService: AuthService,
19 private videoChannelValidatorsService: VideoChannelValidatorsService
20 ) {
21 super()
22 }
23
24 get instanceHost () {
25 return window.location.host
26 }
27
28 isSameThanUsername () {
29 return this.username && this.username === this.form.value['name']
30 }
31
32 ngOnInit () {
33 this.buildForm({
34 name: this.videoChannelValidatorsService.VIDEO_CHANNEL_NAME,
35 displayName: this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME
36 })
37
38 setTimeout(() => this.formBuilt.emit(this.form))
39 }
40}
diff --git a/client/src/app/+signup/+register/register-step-user.component.html b/client/src/app/+signup/+register/register-step-user.component.html
new file mode 100644
index 000000000..cd0c78bfa
--- /dev/null
+++ b/client/src/app/+signup/+register/register-step-user.component.html
@@ -0,0 +1,54 @@
1<form role="form" [formGroup]="form">
2
3 <div class="form-group">
4 <label for="username" i18n>Username</label>
5
6 <div class="input-group">
7 <input
8 type="text" id="username" i18n-placeholder placeholder="Example: jane_doe"
9 formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }"
10 >
11 <div class="input-group-append">
12 <span class="input-group-text">@{{ instanceHost }}</span>
13 </div>
14 </div>
15
16 <div *ngIf="formErrors.username" class="form-error">
17 {{ formErrors.username }}
18 </div>
19 </div>
20
21 <div class="form-group">
22 <label for="email" i18n>Email</label>
23 <input
24 type="text" id="email" i18n-placeholder placeholder="Email"
25 formControlName="email" [ngClass]="{ 'input-error': formErrors['email'] }"
26 >
27 <div *ngIf="formErrors.email" class="form-error">
28 {{ formErrors.email }}
29 </div>
30 </div>
31
32 <div class="form-group">
33 <label for="password" i18n>Password</label>
34 <input
35 type="password" id="password" i18n-placeholder placeholder="Password"
36 formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
37 >
38 <div *ngIf="formErrors.password" class="form-error">
39 {{ formErrors.password }}
40 </div>
41 </div>
42
43 <div class="form-group form-group-terms">
44 <my-peertube-checkbox
45 inputName="terms" formControlName="terms"
46 i18n-labelHtml
47 labelHtml="I am at least 16 years old and agree to the <a href='/about/instance#terms-section' target='_blank'rel='noopener noreferrer'>Terms</a> of this instance"
48 ></my-peertube-checkbox>
49
50 <div *ngIf="formErrors.terms" class="form-error">
51 {{ formErrors.terms }}
52 </div>
53 </div>
54</form>
diff --git a/client/src/app/+signup/+register/register-step-user.component.ts b/client/src/app/+signup/+register/register-step-user.component.ts
new file mode 100644
index 000000000..3825ae371
--- /dev/null
+++ b/client/src/app/+signup/+register/register-step-user.component.ts
@@ -0,0 +1,37 @@
1import { Component, EventEmitter, OnInit, Output } from '@angular/core'
2import { AuthService } from '@app/core'
3import { FormReactive, UserValidatorsService } from '@app/shared'
4import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
5import { FormGroup } from '@angular/forms'
6
7@Component({
8 selector: 'my-register-step-user',
9 templateUrl: './register-step-user.component.html',
10 styleUrls: [ './register.component.scss' ]
11})
12export class RegisterStepUserComponent extends FormReactive implements OnInit {
13 @Output() formBuilt = new EventEmitter<FormGroup>()
14
15 constructor (
16 protected formValidatorService: FormValidatorService,
17 private authService: AuthService,
18 private userValidatorsService: UserValidatorsService
19 ) {
20 super()
21 }
22
23 get instanceHost () {
24 return window.location.host
25 }
26
27 ngOnInit () {
28 this.buildForm({
29 username: this.userValidatorsService.USER_USERNAME,
30 password: this.userValidatorsService.USER_PASSWORD,
31 email: this.userValidatorsService.USER_EMAIL,
32 terms: this.userValidatorsService.USER_TERMS
33 })
34
35 setTimeout(() => this.formBuilt.emit(this.form))
36 }
37}
diff --git a/client/src/app/+signup/+register/register.component.html b/client/src/app/+signup/+register/register.component.html
new file mode 100644
index 000000000..24def68c1
--- /dev/null
+++ b/client/src/app/+signup/+register/register.component.html
@@ -0,0 +1,41 @@
1<div class="margin-content">
2
3 <div i18n class="title-page title-page-single">
4 Create an account
5 </div>
6
7 <my-signup-success *ngIf="signupDone" [message]="success"></my-signup-success>
8 <div *ngIf="info" class="alert alert-info">{{ info }}</div>
9
10 <div class="wrapper" *ngIf="!signupDone">
11 <div>
12 <my-custom-stepper linear *ngIf="!signupDone">
13 <cdk-step [stepControl]="formStepUser" i18n-label label="User information">
14 <my-register-step-user (formBuilt)="onUserFormBuilt($event)"></my-register-step-user>
15
16 <button i18n cdkStepperNext [disabled]="!formStepUser || !formStepUser.valid">Next</button>
17 </cdk-step>
18
19 <cdk-step [stepControl]="formStepChannel" i18n-label label="Channel information">
20 <my-register-step-channel (formBuilt)="onChannelFormBuilt($event)" [username]="getUsername()"></my-register-step-channel>
21
22 <button i18n cdkStepperNext (click)="signup()"
23 [disabled]="!formStepChannel || !formStepChannel.valid || hasSameChannelAndAccountNames()"
24 >
25 Create my account
26 </button>
27 </cdk-step>
28
29 <cdk-step i18n-label label="Done" editable="false">
30 <div *ngIf="error" class="alert alert-danger">{{ error }}</div>
31 </cdk-step>
32 </my-custom-stepper>
33 </div>
34
35 <div>
36 <label i18n>Features found on this instance</label>
37 <my-instance-features-table></my-instance-features-table>
38 </div>
39 </div>
40
41</div>
diff --git a/client/src/app/+signup/+register/register.component.scss b/client/src/app/+signup/+register/register.component.scss
new file mode 100644
index 000000000..6f61b78f7
--- /dev/null
+++ b/client/src/app/+signup/+register/register.component.scss
@@ -0,0 +1,58 @@
1@import '_variables';
2@import '_mixins';
3
4.alert {
5 font-size: 15px;
6 text-align: center;
7}
8
9.wrapper {
10 display: flex;
11 justify-content: space-between;
12 flex-wrap: wrap;
13
14 & > div {
15 margin-bottom: 40px;
16 width: 450px;
17
18 @media screen and (max-width: 500px) {
19 width: auto;
20 }
21 }
22}
23
24my-instance-features-table {
25 display: block;
26
27 margin-bottom: 40px;
28}
29
30.form-group-terms {
31 margin: 30px 0;
32}
33
34.input-group {
35 @include peertube-input-group(400px);
36}
37
38.input-group-append {
39 height: 30px;
40}
41
42input:not([type=submit]) {
43 @include peertube-input-text(400px);
44
45 display: block;
46
47 &#username,
48 &#name {
49 width: auto !important;
50 flex-grow: 1;
51 }
52}
53
54input[type=submit],
55button {
56 @include peertube-button;
57 @include orange-button;
58}
diff --git a/client/src/app/+signup/+register/register.component.ts b/client/src/app/+signup/+register/register.component.ts
new file mode 100644
index 000000000..cd6059728
--- /dev/null
+++ b/client/src/app/+signup/+register/register.component.ts
@@ -0,0 +1,89 @@
1import { Component } from '@angular/core'
2import { AuthService, Notifier, RedirectService, ServerService } from '@app/core'
3import { UserService, UserValidatorsService } from '@app/shared'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5import { UserRegister } from '@shared/models/users/user-register.model'
6import { FormGroup } from '@angular/forms'
7
8@Component({
9 selector: 'my-register',
10 templateUrl: './register.component.html',
11 styleUrls: [ './register.component.scss' ]
12})
13export class RegisterComponent {
14 info: string = null
15 error: string = null
16 success: string = null
17 signupDone = false
18
19 formStepUser: FormGroup
20 formStepChannel: FormGroup
21
22 constructor (
23 private authService: AuthService,
24 private userValidatorsService: UserValidatorsService,
25 private notifier: Notifier,
26 private userService: UserService,
27 private serverService: ServerService,
28 private redirectService: RedirectService,
29 private i18n: I18n
30 ) {
31 }
32
33 get requiresEmailVerification () {
34 return this.serverService.getConfig().signup.requiresEmailVerification
35 }
36
37 hasSameChannelAndAccountNames () {
38 return this.getUsername() === this.getChannelName()
39 }
40
41 getUsername () {
42 if (!this.formStepUser) return undefined
43
44 return this.formStepUser.value['username']
45 }
46
47 getChannelName () {
48 if (!this.formStepChannel) return undefined
49
50 return this.formStepChannel.value['name']
51 }
52
53 onUserFormBuilt (form: FormGroup) {
54 this.formStepUser = form
55 }
56
57 onChannelFormBuilt (form: FormGroup) {
58 this.formStepChannel = form
59 }
60
61 signup () {
62 this.error = null
63
64 const body: UserRegister = Object.assign(this.formStepUser.value, { channel: this.formStepChannel.value })
65
66 this.userService.signup(body).subscribe(
67 () => {
68 this.signupDone = true
69
70 if (this.requiresEmailVerification) {
71 this.info = this.i18n('Now please check your emails to verify your account and complete signup.')
72 return
73 }
74
75 // Auto login
76 this.authService.login(body.username, body.password)
77 .subscribe(
78 () => {
79 this.success = this.i18n('You are now logged in as {{username}}!', { username: body.username })
80 },
81
82 err => this.error = err.message
83 )
84 },
85
86 err => this.error = err.message
87 )
88 }
89}
diff --git a/client/src/app/+signup/+register/register.module.ts b/client/src/app/+signup/+register/register.module.ts
new file mode 100644
index 000000000..46336cbd0
--- /dev/null
+++ b/client/src/app/+signup/+register/register.module.ts
@@ -0,0 +1,33 @@
1import { NgModule } from '@angular/core'
2import { RegisterRoutingModule } from './register-routing.module'
3import { RegisterComponent } from './register.component'
4import { SharedModule } from '@app/shared'
5import { CdkStepperModule } from '@angular/cdk/stepper'
6import { RegisterStepChannelComponent } from './register-step-channel.component'
7import { RegisterStepUserComponent } from './register-step-user.component'
8import { CustomStepperComponent } from './custom-stepper.component'
9import { SignupSharedModule } from '@app/+signup/shared/signup-shared.module'
10
11@NgModule({
12 imports: [
13 RegisterRoutingModule,
14 SharedModule,
15 CdkStepperModule,
16 SignupSharedModule
17 ],
18
19 declarations: [
20 RegisterComponent,
21 CustomStepperComponent,
22 RegisterStepChannelComponent,
23 RegisterStepUserComponent
24 ],
25
26 exports: [
27 RegisterComponent
28 ],
29
30 providers: [
31 ]
32})
33export class RegisterModule { }