aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+signup
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/+signup')
-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
-rw-r--r--client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html22
-rw-r--r--client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.scss12
-rw-r--r--client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts57
-rw-r--r--client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html15
-rw-r--r--client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts50
-rw-r--r--client/src/app/+signup/+verify-account/verify-account-routing.module.ts38
-rw-r--r--client/src/app/+signup/+verify-account/verify-account.module.ts25
-rw-r--r--client/src/app/+signup/shared/signup-shared.module.ts21
-rw-r--r--client/src/app/+signup/shared/signup-success.component.html16
-rw-r--r--client/src/app/+signup/shared/signup-success.component.scss76
-rw-r--r--client/src/app/+signup/shared/signup-success.component.ts10
23 files changed, 882 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 { }
diff --git a/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html
new file mode 100644
index 000000000..2e4180632
--- /dev/null
+++ b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html
@@ -0,0 +1,22 @@
1<div class="margin-content">
2 <div i18n class="title-page title-page-single">
3 Request email for account verification
4 </div>
5
6 <form *ngIf="requiresEmailVerification; else emailVerificationNotRequired" role="form" (ngSubmit)="askSendVerifyEmail()" [formGroup]="form">
7 <div class="form-group">
8 <label i18n for="verify-email-email">Email</label>
9 <input
10 type="email" id="verify-email-email" i18n-placeholder placeholder="Email address" required
11 formControlName="verify-email-email" [ngClass]="{ 'input-error': formErrors['verify-email-email'] }"
12 >
13 <div *ngIf="formErrors['verify-email-email']" class="form-error">
14 {{ formErrors['verify-email-email'] }}
15 </div>
16 </div>
17 <input type="submit" i18n-value value="Send verification email" [disabled]="!form.valid">
18 </form>
19 <ng-template #emailVerificationNotRequired>
20 <div i18n>This instance does not require email verification.</div>
21 </ng-template>
22</div>
diff --git a/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.scss b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.scss
new file mode 100644
index 000000000..efec6b706
--- /dev/null
+++ b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.scss
@@ -0,0 +1,12 @@
1@import '_variables';
2@import '_mixins';
3
4input:not([type=submit]) {
5 @include peertube-input-text(340px);
6 display: block;
7}
8
9input[type=submit] {
10 @include peertube-button;
11 @include orange-button;
12}
diff --git a/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts
new file mode 100644
index 000000000..cfd471fa4
--- /dev/null
+++ b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts
@@ -0,0 +1,57 @@
1import { Component, OnInit } from '@angular/core'
2import { I18n } from '@ngx-translate/i18n-polyfill'
3import { Notifier, RedirectService } from '@app/core'
4import { ServerService } from '@app/core/server'
5import { FormReactive, UserService } from '@app/shared'
6import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
7import { UserValidatorsService } from '@app/shared/forms/form-validators/user-validators.service'
8
9@Component({
10 selector: 'my-verify-account-ask-send-email',
11 templateUrl: './verify-account-ask-send-email.component.html',
12 styleUrls: [ './verify-account-ask-send-email.component.scss' ]
13})
14
15export class VerifyAccountAskSendEmailComponent extends FormReactive implements OnInit {
16
17 constructor (
18 protected formValidatorService: FormValidatorService,
19 private userValidatorsService: UserValidatorsService,
20 private userService: UserService,
21 private serverService: ServerService,
22 private notifier: Notifier,
23 private redirectService: RedirectService,
24 private i18n: I18n
25 ) {
26 super()
27 }
28
29 get requiresEmailVerification () {
30 return this.serverService.getConfig().signup.requiresEmailVerification
31 }
32
33 ngOnInit () {
34 this.buildForm({
35 'verify-email-email': this.userValidatorsService.USER_EMAIL
36 })
37 }
38
39 askSendVerifyEmail () {
40 const email = this.form.value['verify-email-email']
41 this.userService.askSendVerifyEmail(email)
42 .subscribe(
43 () => {
44 const message = this.i18n(
45 'An email with verification link will be sent to {{email}}.',
46 { email }
47 )
48 this.notifier.success(message)
49 this.redirectService.redirectToHomepage()
50 },
51
52 err => {
53 this.notifier.error(err.message)
54 }
55 )
56 }
57}
diff --git a/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html
new file mode 100644
index 000000000..728709ca6
--- /dev/null
+++ b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html
@@ -0,0 +1,15 @@
1<div class="margin-content">
2 <div i18n class="title-page title-page-single">
3 Verify account email confirmation
4 </div>
5
6 <my-signup-success i18n *ngIf="success; else verificationError" message="Your email has been verified and you may now login.">
7 </my-signup-success>
8
9 <ng-template #verificationError>
10 <div>
11 <span i18n>An error occurred. </span>
12 <a i18n routerLink="/verify-account/ask-send-email">Request new verification email.</a>
13 </div>
14 </ng-template>
15</div>
diff --git a/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts
new file mode 100644
index 000000000..3fb2d1cd8
--- /dev/null
+++ b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts
@@ -0,0 +1,50 @@
1import { Component, OnInit } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { Notifier } from '@app/core'
5import { UserService } from '@app/shared'
6
7@Component({
8 selector: 'my-verify-account-email',
9 templateUrl: './verify-account-email.component.html'
10})
11
12export class VerifyAccountEmailComponent implements OnInit {
13 success = false
14
15 private userId: number
16 private verificationString: string
17
18 constructor (
19 private userService: UserService,
20 private notifier: Notifier,
21 private router: Router,
22 private route: ActivatedRoute,
23 private i18n: I18n
24 ) {
25 }
26
27 ngOnInit () {
28 this.userId = this.route.snapshot.queryParams['userId']
29 this.verificationString = this.route.snapshot.queryParams['verificationString']
30
31 if (!this.userId || !this.verificationString) {
32 this.notifier.error(this.i18n('Unable to find user id or verification string.'))
33 } else {
34 this.verifyEmail()
35 }
36 }
37
38 verifyEmail () {
39 this.userService.verifyEmail(this.userId, this.verificationString)
40 .subscribe(
41 () => {
42 this.success = true
43 },
44
45 err => {
46 this.notifier.error(err.message)
47 }
48 )
49 }
50}
diff --git a/client/src/app/+signup/+verify-account/verify-account-routing.module.ts b/client/src/app/+signup/+verify-account/verify-account-routing.module.ts
new file mode 100644
index 000000000..16d5fe0d0
--- /dev/null
+++ b/client/src/app/+signup/+verify-account/verify-account-routing.module.ts
@@ -0,0 +1,38 @@
1import { NgModule } from '@angular/core'
2import { RouterModule, Routes } from '@angular/router'
3import { MetaGuard } from '@ngx-meta/core'
4import { VerifyAccountEmailComponent } from './verify-account-email/verify-account-email.component'
5import { VerifyAccountAskSendEmailComponent } from './verify-account-ask-send-email/verify-account-ask-send-email.component'
6
7const verifyAccountRoutes: Routes = [
8 {
9 path: '',
10 canActivateChild: [ MetaGuard ],
11 children: [
12 {
13 path: 'email',
14 component: VerifyAccountEmailComponent,
15 data: {
16 meta: {
17 title: 'Verify account email'
18 }
19 }
20 },
21 {
22 path: 'ask-send-email',
23 component: VerifyAccountAskSendEmailComponent,
24 data: {
25 meta: {
26 title: 'Verify account ask send email'
27 }
28 }
29 }
30 ]
31 }
32]
33
34@NgModule({
35 imports: [ RouterModule.forChild(verifyAccountRoutes) ],
36 exports: [ RouterModule ]
37})
38export class VerifyAccountRoutingModule {}
diff --git a/client/src/app/+signup/+verify-account/verify-account.module.ts b/client/src/app/+signup/+verify-account/verify-account.module.ts
new file mode 100644
index 000000000..9fe14e81e
--- /dev/null
+++ b/client/src/app/+signup/+verify-account/verify-account.module.ts
@@ -0,0 +1,25 @@
1import { NgModule } from '@angular/core'
2import { VerifyAccountRoutingModule } from './verify-account-routing.module'
3import { VerifyAccountEmailComponent } from './verify-account-email/verify-account-email.component'
4import { VerifyAccountAskSendEmailComponent } from './verify-account-ask-send-email/verify-account-ask-send-email.component'
5import { SharedModule } from '@app/shared'
6import { SignupSharedModule } from '@app/+signup/shared/signup-shared.module'
7
8@NgModule({
9 imports: [
10 VerifyAccountRoutingModule,
11 SharedModule,
12 SignupSharedModule
13 ],
14
15 declarations: [
16 VerifyAccountEmailComponent,
17 VerifyAccountAskSendEmailComponent
18 ],
19
20 exports: [],
21
22 providers: []
23})
24export class VerifyAccountModule {
25}
diff --git a/client/src/app/+signup/shared/signup-shared.module.ts b/client/src/app/+signup/shared/signup-shared.module.ts
new file mode 100644
index 000000000..cd21fdef3
--- /dev/null
+++ b/client/src/app/+signup/shared/signup-shared.module.ts
@@ -0,0 +1,21 @@
1import { NgModule } from '@angular/core'
2import { SignupSuccessComponent } from '../shared/signup-success.component'
3import { SharedModule } from '@app/shared'
4
5@NgModule({
6 imports: [
7 SharedModule
8 ],
9
10 declarations: [
11 SignupSuccessComponent
12 ],
13
14 exports: [
15 SignupSuccessComponent
16 ],
17
18 providers: [
19 ]
20})
21export class SignupSharedModule { }
diff --git a/client/src/app/+signup/shared/signup-success.component.html b/client/src/app/+signup/shared/signup-success.component.html
new file mode 100644
index 000000000..e35f858c6
--- /dev/null
+++ b/client/src/app/+signup/shared/signup-success.component.html
@@ -0,0 +1,16 @@
1<!-- Thanks: Amit Singh Sansoya from https://codepen.io/amit3200/pen/zWMJOO -->
2
3<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 130.2 130.2">
4 <circle class="path circle" fill="none" stroke="#73AF55" stroke-width="6" stroke-miterlimit="10" cx="65.1" cy="65.1" r="62.1"/>
5 <polyline class="path check" fill="none" stroke="#73AF55" stroke-width="6" stroke-linecap="round" stroke-miterlimit="10" points="100.2,40.2 51.5,88.8 29.8,67.5 "/>
6</svg>
7
8<p class="bottom-message">Welcome on PeerTube!</p>
9
10<div *ngIf="message" class="alert alert-success">
11 <p>{{ message }}</p>
12
13 <p i18n>
14 If you need help to use PeerTube, you can take a look to the <a href="https://docs.joinpeertube.org/#/use-setup-account" target="_blank" rel="noopener noreferrer">documentation</a>.
15 </p>
16</div>
diff --git a/client/src/app/+signup/shared/signup-success.component.scss b/client/src/app/+signup/shared/signup-success.component.scss
new file mode 100644
index 000000000..fbc27c8bc
--- /dev/null
+++ b/client/src/app/+signup/shared/signup-success.component.scss
@@ -0,0 +1,76 @@
1svg {
2 width: 100px;
3 display: block;
4 margin: 40px auto 0;
5}
6
7.path {
8 stroke-dasharray: 1000;
9 stroke-dashoffset: 0;
10
11 &.circle {
12 -webkit-animation: dash .9s ease-in-out;
13 animation: dash .9s ease-in-out;
14 }
15
16 &.line {
17 stroke-dashoffset: 1000;
18 -webkit-animation: dash .9s .35s ease-in-out forwards;
19 animation: dash .9s .35s ease-in-out forwards;
20 }
21
22 &.check {
23 stroke-dashoffset: -100;
24 -webkit-animation: dash-check .9s .35s ease-in-out forwards;
25 animation: dash-check .9s .35s ease-in-out forwards;
26 }
27}
28
29.bottom-message {
30 text-align: center;
31 margin: 20px 0 60px;
32 font-size: 1.25em;
33 color: #73AF55;
34}
35
36.alert {
37 font-size: 15px;
38 text-align: center;
39}
40
41
42@-webkit-keyframes dash {
43 0% {
44 stroke-dashoffset: 1000;
45 }
46 100% {
47 stroke-dashoffset: 0;
48 }
49}
50
51@keyframes dash {
52 0% {
53 stroke-dashoffset: 1000;
54 }
55 100% {
56 stroke-dashoffset: 0;
57 }
58}
59
60@-webkit-keyframes dash-check {
61 0% {
62 stroke-dashoffset: -100;
63 }
64 100% {
65 stroke-dashoffset: 900;
66 }
67}
68
69@keyframes dash-check {
70 0% {
71 stroke-dashoffset: -100;
72 }
73 100% {
74 stroke-dashoffset: 900;
75 }
76}
diff --git a/client/src/app/+signup/shared/signup-success.component.ts b/client/src/app/+signup/shared/signup-success.component.ts
new file mode 100644
index 000000000..19fb5922a
--- /dev/null
+++ b/client/src/app/+signup/shared/signup-success.component.ts
@@ -0,0 +1,10 @@
1import { Component, Input } from '@angular/core'
2
3@Component({
4 selector: 'my-signup-success',
5 templateUrl: './signup-success.component.html',
6 styleUrls: [ './signup-success.component.scss' ]
7})
8export class SignupSuccessComponent {
9 @Input() message: string
10}