diff options
author | Chocobozzz <me@florianbigard.com> | 2022-06-14 13:54:54 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2022-06-15 13:31:53 +0200 |
commit | 6f03f944c34f78b38a68128413b55186e0676949 (patch) | |
tree | 81a0c79184cb5ac800c31f1d5334471ee506ac19 /client/src/app | |
parent | 936ce6e5635f3a52acbc799e1fcba9a948a7e390 (diff) | |
download | PeerTube-6f03f944c34f78b38a68128413b55186e0676949.tar.gz PeerTube-6f03f944c34f78b38a68128413b55186e0676949.tar.zst PeerTube-6f03f944c34f78b38a68128413b55186e0676949.zip |
Redesign register steps
Diffstat (limited to 'client/src/app')
35 files changed, 721 insertions, 369 deletions
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.html b/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.html index 3d8ab094f..2a965ac97 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.html | |||
@@ -4,7 +4,7 @@ | |||
4 | <div class="col-12 col-lg-4 col-xl-3"></div> | 4 | <div class="col-12 col-lg-4 col-xl-3"></div> |
5 | <div class="col-12 col-lg-8"> | 5 | <div class="col-12 col-lg-8"> |
6 | 6 | ||
7 | <div class="callout callout-info"> | 7 | <div class="callout callout-orange"> |
8 | <span i18n> | 8 | <span i18n> |
9 | Estimating a server's capacity to transcode and stream videos isn't easy and we can't tune PeerTube automatically. | 9 | Estimating a server's capacity to transcode and stream videos isn't easy and we can't tune PeerTube automatically. |
10 | </span> | 10 | </span> |
diff --git a/client/src/app/+signup/+register/custom-stepper.component.html b/client/src/app/+signup/+register/custom-stepper.component.html index a07e2fca3..f43a46842 100644 --- a/client/src/app/+signup/+register/custom-stepper.component.html +++ b/client/src/app/+signup/+register/custom-stepper.component.html | |||
@@ -1,24 +1,29 @@ | |||
1 | <section class="container"> | 1 | <section> |
2 | <header *ngIf="steps.length > 2"> | 2 | <header *ngIf="steps.length > 2"> |
3 | <ng-container *ngFor="let step of steps; let i = index; let isLast = last;"> | 3 | <div class="header-steps"> |
4 | <div | 4 | <ng-container *ngFor="let step of steps; let i = index; let isLast = last;"> |
5 | class="step-info" [ngClass]="{ active: selectedIndex === i, completed: isCompleted(step), 'c-hand': isAccessible(i) }" [attr.aria-current]="selectedIndex === i" | 5 | <div |
6 | (click)="onClick(i)" | 6 | class="step-info" [ngClass]="{ active: selectedIndex === i, completed: isCompleted(step), 'c-hand': isAccessible(step) }" [attr.aria-current]="selectedIndex === i" |
7 | > | 7 | (click)="onClick(i)" |
8 | <div class="step-index"> | 8 | > |
9 | <ng-container *ngIf="!isCompleted(step)"><span class="visually-hidden" i18n>Step</span> {{ i + 1 }}</ng-container> | 9 | <div class="step-index"> |
10 | <my-global-icon *ngIf="isCompleted(step)" iconName="tick"></my-global-icon> | 10 | <span class="visually-hidden" i18n>Step</span> {{ i + 1 }} |
11 | </div> | 11 | |
12 | <div class="completed-icon" *ngIf="isCompleted(step)"> | ||
13 | <my-global-icon iconName="tick"></my-global-icon> | ||
14 | </div> | ||
15 | </div> | ||
12 | 16 | ||
13 | <div class="step-label">{{ step.label }}</div> | 17 | <div class="step-label">{{ step.label }}</div> |
14 | </div> | 18 | </div> |
15 | 19 | ||
16 | <!-- Do no display if this is the last child --> | 20 | <!-- Do no display if this is the last child --> |
17 | <div *ngIf="!isLast" class="connector"></div> | 21 | <div *ngIf="!isLast" class="connector"></div> |
18 | </ng-container> | 22 | </ng-container> |
23 | </div> | ||
19 | </header> | 24 | </header> |
20 | 25 | ||
21 | <div [style.display]="selected ? 'block' : 'none'"> | 26 | <div class="margin-content" [style.display]="selected ? 'block' : 'none'"> |
22 | <ng-container [ngTemplateOutlet]="selected.content"></ng-container> | 27 | <ng-container [ngTemplateOutlet]="selected.content"></ng-container> |
23 | </div> | 28 | </div> |
24 | 29 | ||
diff --git a/client/src/app/+signup/+register/custom-stepper.component.scss b/client/src/app/+signup/+register/custom-stepper.component.scss index 6a8815c77..4dda93489 100644 --- a/client/src/app/+signup/+register/custom-stepper.component.scss +++ b/client/src/app/+signup/+register/custom-stepper.component.scss | |||
@@ -2,76 +2,113 @@ | |||
2 | @use '_variables' as *; | 2 | @use '_variables' as *; |
3 | @use '_mixins' as *; | 3 | @use '_mixins' as *; |
4 | 4 | ||
5 | $grey-color: #9CA3AB; | 5 | $index-block-height: 40px; |
6 | $index-block-height: 32px; | ||
7 | 6 | ||
8 | .container { | 7 | header { |
9 | @include padding-left(0); | 8 | margin-bottom: 40px; |
10 | @include padding-right(0); | 9 | padding-bottom: 60px; |
11 | max-width: unset !important; | 10 | width: 100%; |
11 | background-color: pvar(--mainColorVeryLight); | ||
12 | } | 12 | } |
13 | 13 | ||
14 | header { | 14 | .header-steps { |
15 | max-width: 800px; | ||
15 | display: flex; | 16 | display: flex; |
16 | justify-content: space-between; | 17 | justify-content: space-between; |
17 | font-size: 15px; | 18 | margin: auto; |
18 | margin-bottom: 30px; | 19 | |
20 | // Useful on small screens | ||
21 | padding: 0 20px; | ||
22 | } | ||
19 | 23 | ||
20 | .step-info { | 24 | .step-index { |
21 | color: $grey-color; | 25 | display: flex; |
26 | justify-content: center; | ||
27 | align-items: center; | ||
28 | width: $index-block-height; | ||
29 | height: $index-block-height; | ||
30 | border-radius: $index-block-height; | ||
31 | border: 1px solid pvar(--mainColor); | ||
32 | margin-bottom: 10px; | ||
33 | font-size: 24px; | ||
34 | position: relative; | ||
35 | |||
36 | .completed-icon { | ||
37 | width: 16px; | ||
38 | height: 16px; | ||
39 | border-radius: 16px; | ||
40 | background-color: pvar(--mainBackgroundColor); | ||
41 | position: absolute; | ||
42 | bottom: 0; | ||
43 | right: 0; | ||
22 | display: flex; | 44 | display: flex; |
23 | flex-direction: column; | 45 | justify-content: center; |
24 | align-items: center; | 46 | align-items: center; |
25 | width: $index-block-height; | 47 | border: 1px solid pvar(--mainColor); |
48 | |||
49 | my-global-icon { | ||
50 | @include apply-svg-color(pvar(--mainColor)); | ||
26 | 51 | ||
27 | &:not(.c-hand) { | 52 | display: flex; |
28 | cursor: default; | 53 | width: 12px; |
54 | height: 12px; | ||
29 | } | 55 | } |
56 | } | ||
57 | } | ||
30 | 58 | ||
59 | .step-label { | ||
60 | width: max-content; | ||
61 | font-size: 18px; | ||
62 | } | ||
63 | |||
64 | .step-info { | ||
65 | color: pvar(--mainColor); | ||
66 | display: flex; | ||
67 | flex-direction: column; | ||
68 | align-items: center; | ||
69 | width: $index-block-height; | ||
70 | opacity: 0.5; | ||
71 | cursor: default; | ||
72 | |||
73 | &.c-hand { | ||
74 | cursor: pointer; | ||
75 | } | ||
76 | |||
77 | &.active, | ||
78 | &.completed { | ||
31 | .step-index { | 79 | .step-index { |
32 | display: flex; | 80 | background-color: pvar(--mainColor); |
33 | justify-content: center; | 81 | color: pvar(--mainBackgroundColor); |
34 | align-items: center; | ||
35 | width: $index-block-height; | ||
36 | height: $index-block-height; | ||
37 | border-radius: 100px; | ||
38 | border: 2px solid $grey-color; | ||
39 | margin-bottom: 10px; | ||
40 | |||
41 | my-global-icon { | ||
42 | @include apply-svg-color(pvar(--mainBackgroundColor)); | ||
43 | |||
44 | width: 22px; | ||
45 | height: 22px; | ||
46 | } | ||
47 | } | 82 | } |
48 | 83 | ||
49 | .step-label { | 84 | .step-label { |
50 | width: max-content; | 85 | color: pvar(--mainColor); |
51 | } | 86 | } |
87 | } | ||
52 | 88 | ||
53 | &.active, | 89 | &.active { |
54 | &.completed { | 90 | opacity: 1; |
55 | .step-index { | 91 | } |
56 | border-color: pvar(--mainColor); | 92 | } |
57 | background-color: pvar(--mainColor); | ||
58 | color: pvar(--mainBackgroundColor); | ||
59 | } | ||
60 | |||
61 | .step-label { | ||
62 | color: pvar(--mainColor); | ||
63 | } | ||
64 | } | ||
65 | 93 | ||
66 | &.completed { | 94 | .connector { |
67 | cursor: pointer; | 95 | flex: auto; |
68 | } | 96 | margin: math.div($index-block-height, 2) 10px 0 10px; |
97 | height: 2px; | ||
98 | background-color: pvar(--mainColor); | ||
99 | opacity: 0.3; | ||
100 | } | ||
101 | |||
102 | @media screen and (min-width: $small-view) { | ||
103 | .margin-content { | ||
104 | max-width: 1000px; | ||
105 | margin:auto; | ||
69 | } | 106 | } |
107 | } | ||
70 | 108 | ||
71 | .connector { | 109 | @media screen and (max-width: $small-view) { |
72 | flex: auto; | 110 | .step-label { |
73 | margin: math.div($index-block-height, 2) 10px 0 10px; | 111 | width: auto; |
74 | height: 2px; | 112 | text-align: center; |
75 | background-color: $grey-color; | ||
76 | } | 113 | } |
77 | } | 114 | } |
diff --git a/client/src/app/+signup/+register/custom-stepper.component.ts b/client/src/app/+signup/+register/custom-stepper.component.ts index 3b7ba40e8..4c308f7b6 100644 --- a/client/src/app/+signup/+register/custom-stepper.component.ts +++ b/client/src/app/+signup/+register/custom-stepper.component.ts | |||
@@ -14,13 +14,10 @@ export class CustomStepperComponent extends CdkStepper { | |||
14 | } | 14 | } |
15 | 15 | ||
16 | isCompleted (step: CdkStep) { | 16 | isCompleted (step: CdkStep) { |
17 | return step.stepControl?.dirty && step.stepControl.valid | 17 | return step.completed |
18 | } | 18 | } |
19 | 19 | ||
20 | isAccessible (index: number) { | 20 | isAccessible (step: CdkStep) { |
21 | const stepsCompletedMap = this.steps.map(step => this.isCompleted(step)) | 21 | return step.editable && step.completed |
22 | return index === 0 | ||
23 | ? true | ||
24 | : stepsCompletedMap[index - 1] | ||
25 | } | 22 | } |
26 | } | 23 | } |
diff --git a/client/src/app/+signup/+register/register-step-channel.component.html b/client/src/app/+signup/+register/register-step-channel.component.html deleted file mode 100644 index 888e3245d..000000000 --- a/client/src/app/+signup/+register/register-step-channel.component.html +++ /dev/null | |||
@@ -1,52 +0,0 @@ | |||
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 i18n> | ||
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="displayName" i18n>Channel display name</label> | ||
16 | |||
17 | <div class="input-group"> | ||
18 | <input | ||
19 | type="text" id="displayName" | ||
20 | formControlName="displayName" [ngClass]="{ 'input-error': formErrors['displayName'] }" | ||
21 | > | ||
22 | </div> | ||
23 | |||
24 | <div *ngIf="formErrors.displayName" class="form-error"> | ||
25 | {{ formErrors.displayName }} | ||
26 | </div> | ||
27 | </div> | ||
28 | |||
29 | <div class="form-group"> | ||
30 | <label for="name" i18n>Channel name</label> | ||
31 | |||
32 | <div class="input-group"> | ||
33 | <input | ||
34 | type="text" id="name" i18n-placeholder placeholder="Example: my_super_channel" | ||
35 | formControlName="name" [ngClass]="{ 'input-error': formErrors['name'] }" | ||
36 | > | ||
37 | <div class="input-group-text">@{{ instanceHost }}</div> | ||
38 | </div> | ||
39 | |||
40 | <div class="name-information" i18n> | ||
41 | The channel name is a unique identifier of your channel on this and all the other instances. It's as unique as an email address, which makes it easy for other people to interact with it. | ||
42 | </div> | ||
43 | |||
44 | <div *ngIf="formErrors.name" class="form-error"> | ||
45 | {{ formErrors.name }} | ||
46 | </div> | ||
47 | |||
48 | <div *ngIf="isSameThanUsername()" class="form-error" i18n> | ||
49 | Channel name cannot be the same as your account name. You can click on the first step to update your account name. | ||
50 | </div> | ||
51 | </div> | ||
52 | </form> | ||
diff --git a/client/src/app/+signup/+register/register-step-user.component.html b/client/src/app/+signup/+register/register-step-user.component.html deleted file mode 100644 index 6e367b4c7..000000000 --- a/client/src/app/+signup/+register/register-step-user.component.html +++ /dev/null | |||
@@ -1,64 +0,0 @@ | |||
1 | <form role="form" [formGroup]="form"> | ||
2 | |||
3 | <div class="capability-information alert alert-info" i18n *ngIf="videoUploadDisabled"> | ||
4 | Video uploads are disabled on this instance, hence your account won't be able to upload videos. | ||
5 | </div> | ||
6 | |||
7 | <div class="form-group"> | ||
8 | <label for="displayName" i18n>Display name</label> | ||
9 | |||
10 | <div class="input-group"> | ||
11 | <input | ||
12 | type="text" id="displayName" placeholder="John Doe" | ||
13 | formControlName="displayName" [ngClass]="{ 'input-error': formErrors['displayName'] }" | ||
14 | > | ||
15 | </div> | ||
16 | |||
17 | <div *ngIf="formErrors.displayName" class="form-error"> | ||
18 | {{ formErrors.displayName }} | ||
19 | </div> | ||
20 | </div> | ||
21 | |||
22 | <div class="form-group"> | ||
23 | <label for="username" i18n>Username</label> | ||
24 | |||
25 | <div class="input-group"> | ||
26 | <input | ||
27 | type="text" id="username" i18n-placeholder="Username choice placeholder in the registration form" placeholder="e.g. jane_doe" | ||
28 | formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" | ||
29 | > | ||
30 | <span class="input-group-text">@{{ instanceHost }}</span> | ||
31 | </div> | ||
32 | |||
33 | <div class="name-information" i18n> | ||
34 | The username is a unique identifier of your account on this and all the other instances. It's as unique as an email address, which makes it easy for other people to interact with it. | ||
35 | </div> | ||
36 | |||
37 | <div *ngIf="formErrors.username" class="form-error"> | ||
38 | {{ formErrors.username }} | ||
39 | </div> | ||
40 | </div> | ||
41 | |||
42 | <div class="form-group"> | ||
43 | <label for="email" i18n>Email</label> | ||
44 | <input | ||
45 | type="text" id="email" i18n-placeholder placeholder="Email" | ||
46 | formControlName="email" class="form-control" [ngClass]="{ 'input-error': formErrors['email'] }" | ||
47 | > | ||
48 | <div *ngIf="formErrors.email" class="form-error"> | ||
49 | {{ formErrors.email }} | ||
50 | </div> | ||
51 | </div> | ||
52 | |||
53 | <div class="form-group"> | ||
54 | <label for="password" i18n>Password</label> | ||
55 | <my-input-text formControlName="password" inputId="password" | ||
56 | i18n-placeholder placeholder="Password" | ||
57 | [ngClass]="{ 'input-error': formErrors['password'] }" | ||
58 | autocomplete="new-password"></my-input-text> | ||
59 | <div *ngIf="formErrors.password" class="form-error"> | ||
60 | {{ formErrors.password }} | ||
61 | </div> | ||
62 | </div> | ||
63 | |||
64 | </form> | ||
diff --git a/client/src/app/+signup/+register/register.component.html b/client/src/app/+signup/+register/register.component.html index 5c4fe5f0b..76b145604 100644 --- a/client/src/app/+signup/+register/register.component.html +++ b/client/src/app/+signup/+register/register.component.html | |||
@@ -1,64 +1,121 @@ | |||
1 | <div class="margin-content"> | 1 | <div> |
2 | 2 | ||
3 | <div class="signup-disabled" *ngIf="signupDisabled"> | 3 | <div class="signup-disabled" *ngIf="signupDisabled"> |
4 | <div class="alert alert-warning" i18n>Signup is not enabled on this instance.</div> | 4 | <div class="alert alert-warning" i18n>Signup is not enabled on this instance.</div> |
5 | </div> | 5 | </div> |
6 | 6 | ||
7 | <ng-container *ngIf="!signupDisabled"> | 7 | <ng-container *ngIf="!signupDisabled"> |
8 | <div i18n class="title-page title-page-single"> | 8 | <h1 i18n class="header-title"> |
9 | <strong class="underline-orange">{{ instanceName }}</strong> | ||
10 | > | ||
9 | Create an account | 11 | Create an account |
10 | </div> | 12 | </h1> |
11 | 13 | ||
12 | <my-signup-success *ngIf="signupDone" [message]="success"></my-signup-success> | 14 | <div class="register-content"> |
13 | <div *ngIf="info" class="alert alert-info">{{ info }}</div> | 15 | <my-custom-stepper linear> |
14 | 16 | ||
15 | <div class="wrapper" [hidden]="signupDone"> | 17 | <cdk-step i18n-label label="About" [editable]="!signupSuccess"> |
16 | <div class="register-form"> | 18 | <my-signup-step-title mascotImageName="about" i18n> |
17 | <my-custom-stepper linear *ngIf="!signupDone"> | 19 | <strong>Create an account</strong> |
18 | <cdk-step [stepControl]="formStepTerms" i18n-label="Stepper label for the registration page describing terms of service" label="Terms"> | 20 | <div>on {{ instanceName }}</div> |
19 | <div class="instance-information"> | 21 | </my-signup-step-title> |
20 | <my-instance-about-accordion | 22 | |
21 | (init)="onInstanceAboutAccordionInit($event)" [panels]="instanceInformationPanels" | 23 | <my-register-step-about [videoUploadDisabled]="videoUploadDisabled"></my-register-step-about> |
22 | pluginScope="signup" pluginHook="filter:signup.instance-about-plugin-panels.create.result" | 24 | |
23 | ></my-instance-about-accordion> | 25 | <div class="step-buttons"> |
24 | </div> | 26 | <a i18n class="skip-step underline-orange" routerLink="/login"> |
27 | <strong>I already have an account</strong>, I log in | ||
28 | </a> | ||
25 | 29 | ||
26 | <my-register-step-terms | 30 | <button i18n cdkStepperNext>I create an account</button> |
27 | [hasCodeOfConduct]="!!aboutHtml.codeOfConduct" | 31 | </div> |
28 | [minimumAge]="minimumAge" | 32 | </cdk-step> |
29 | (formBuilt)="onTermsFormBuilt($event)" (termsClick)="onTermsClick()" (codeOfConductClick)="onCodeOfConductClick()" | ||
30 | ></my-register-step-terms> | ||
31 | 33 | ||
34 | <cdk-step [stepControl]="formStepTerms" i18n-label label="Terms" [editable]="!signupSuccess"> | ||
35 | <my-signup-step-title mascotImageName="terms" i18n> | ||
36 | <strong>Terms</strong> | ||
37 | <div>of {{ instanceName }}</div> | ||
38 | </my-signup-step-title> | ||
39 | |||
40 | <my-instance-about-accordion | ||
41 | [displayInstanceName]="false" | ||
42 | (init)="onInstanceAboutAccordionInit($event)" [panels]="instanceInformationPanels" | ||
43 | pluginScope="signup" pluginHook="filter:signup.instance-about-plugin-panels.create.result" | ||
44 | ></my-instance-about-accordion> | ||
45 | |||
46 | <my-register-step-terms | ||
47 | [hasCodeOfConduct]="!!aboutHtml.codeOfConduct" | ||
48 | [minimumAge]="minimumAge" | ||
49 | (formBuilt)="onTermsFormBuilt($event)" (termsClick)="onTermsClick()" (codeOfConductClick)="onCodeOfConductClick()" | ||
50 | ></my-register-step-terms> | ||
51 | |||
52 | <div class="step-buttons"> | ||
53 | <button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button> | ||
32 | <button cdkStepperNext [disabled]="!formStepTerms || !formStepTerms.valid">{{ defaultNextStepButtonLabel }}</button> | 54 | <button cdkStepperNext [disabled]="!formStepTerms || !formStepTerms.valid">{{ defaultNextStepButtonLabel }}</button> |
33 | </cdk-step> | 55 | </div> |
56 | </cdk-step> | ||
57 | |||
58 | <cdk-step [stepControl]="formStepUser" label="My account" [editable]="!signupSuccess"> | ||
59 | <my-signup-step-title mascotImageName="account" i18n> | ||
60 | <strong>Setup</strong> | ||
61 | <div>your account</div> | ||
62 | </my-signup-step-title> | ||
34 | 63 | ||
35 | <cdk-step [stepControl]="formStepUser" i18n-label="Stepper label for the registration page asking user information" label="User"> | 64 | <my-register-step-user |
36 | <my-register-step-user (formBuilt)="onUserFormBuilt($event)" [videoUploadDisabled]="videoUploadDisabled"></my-register-step-user> | 65 | (formBuilt)="onUserFormBuilt($event)" |
66 | [videoUploadDisabled]="videoUploadDisabled" [requiresEmailVerification]="requiresEmailVerification" | ||
67 | ></my-register-step-user> | ||
37 | 68 | ||
69 | <div class="step-buttons"> | ||
38 | <button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button> | 70 | <button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button> |
39 | <button cdkStepperNext [disabled]="!formStepUser || !formStepUser.valid" (click)="videoUploadDisabled && signup()">{{ stepUserButtonLabel }}</button> | 71 | <button cdkStepperNext [disabled]="!formStepUser || !formStepUser.valid" (click)="videoUploadDisabled && signup()">{{ stepUserButtonLabel }}</button> |
40 | </cdk-step> | 72 | </div> |
73 | </cdk-step> | ||
41 | 74 | ||
42 | <cdk-step [stepControl]="formStepChannel" i18n-label="Stepper label for the registration page asking information about the default channel" label="Channel" *ngIf="!videoUploadDisabled"> | 75 | <cdk-step *ngIf="!videoUploadDisabled" [optional]="true" [stepControl]="formStepChannel" i18n-label label="My channel" [editable]="!signupSuccess"> |
43 | <my-register-step-channel (formBuilt)="onChannelFormBuilt($event)" [username]="getUsername()"></my-register-step-channel> | 76 | <my-signup-step-title mascotImageName="channel" i18n> |
77 | <div>Create</div> | ||
78 | <strong>your first channel</strong> | ||
79 | </my-signup-step-title> | ||
44 | 80 | ||
81 | <my-register-step-channel | ||
82 | (formBuilt)="onChannelFormBuilt($event)" | ||
83 | [videoQuota]="videoQuota" [instanceName]="instanceName" [username]="getUsername()" | ||
84 | ></my-register-step-channel> | ||
85 | |||
86 | <div class="step-buttons"> | ||
45 | <button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button> | 87 | <button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button> |
88 | |||
89 | <div class="skip-step"> | ||
90 | <span class="underline-orange" role="button" (click)="skipChannelCreation()"> | ||
91 | <strong i18n>I don't want to create a channel</strong> | ||
92 | </span> | ||
93 | |||
94 | <div class="skip-step-description" i18n>You will be able to create a channel later</div> | ||
95 | </div> | ||
96 | |||
46 | <button cdkStepperNext [disabled]="!formStepChannel || !formStepChannel.valid || hasSameChannelAndAccountNames()" (click)="signup()" i18n> | 97 | <button cdkStepperNext [disabled]="!formStepChannel || !formStepChannel.valid || hasSameChannelAndAccountNames()" (click)="signup()" i18n> |
47 | Create my account | 98 | Create my account |
48 | </button> | 99 | </button> |
49 | </cdk-step> | 100 | </div> |
101 | </cdk-step> | ||
50 | 102 | ||
51 | <cdk-step i18n-label label="Done" editable="false"> | 103 | <cdk-step #lastStep i18n-label label="Done!" [editable]="false"> |
52 | <div *ngIf="!signupDone && !error" class="done-loader"> | 104 | <div *ngIf="!signupSuccess && !signupError" class="done-loader"> |
53 | <my-loader [loading]="true"></my-loader> | 105 | <my-loader [loading]="true"></my-loader> |
54 | 106 | ||
55 | <div i18n>PeerTube is creating your account...</div> | 107 | <div i18n>PeerTube is creating your account...</div> |
56 | </div> | 108 | </div> |
109 | |||
110 | <div *ngIf="signupError" class="alert alert-danger">{{ signupError }}</div> | ||
57 | 111 | ||
58 | <div *ngIf="error" class="alert alert-danger">{{ error }}</div> | 112 | <my-signup-success *ngIf="signupSuccess" [requiresEmailVerification]="requiresEmailVerification"></my-signup-success> |
59 | </cdk-step> | 113 | |
60 | </my-custom-stepper> | 114 | <div *ngIf="signupError" class="steps-button"> |
61 | </div> | 115 | <button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button> |
116 | </div> | ||
117 | </cdk-step> | ||
118 | </my-custom-stepper> | ||
62 | </div> | 119 | </div> |
63 | </ng-container> | 120 | </ng-container> |
64 | 121 | ||
diff --git a/client/src/app/+signup/+register/register.component.scss b/client/src/app/+signup/+register/register.component.scss index 53093a81a..5d0df81bd 100644 --- a/client/src/app/+signup/+register/register.component.scss +++ b/client/src/app/+signup/+register/register.component.scss | |||
@@ -2,7 +2,7 @@ | |||
2 | @use '_mixins' as *; | 2 | @use '_mixins' as *; |
3 | 3 | ||
4 | .alert { | 4 | .alert { |
5 | font-size: 15px; | 5 | font-size: 16px; |
6 | text-align: center; | 6 | text-align: center; |
7 | } | 7 | } |
8 | 8 | ||
@@ -10,61 +10,75 @@ | |||
10 | padding-top: 30vh; | 10 | padding-top: 30vh; |
11 | } | 11 | } |
12 | 12 | ||
13 | .wrapper { | 13 | .header-title { |
14 | font-weight: normal; | ||
15 | font-size: 15px; | ||
16 | background-color: pvar(--mainColorVeryLight); | ||
17 | padding: 35px 25px 15px 25px; | ||
18 | margin: 0; | ||
19 | } | ||
20 | |||
21 | .register-content { | ||
22 | font-size: 16px; | ||
23 | } | ||
24 | |||
25 | my-instance-about-accordion { | ||
26 | display: block; | ||
27 | margin-bottom: 25px; | ||
28 | } | ||
29 | |||
30 | .step-buttons { | ||
14 | display: flex; | 31 | display: flex; |
15 | flex-direction: column; | 32 | flex-wrap: wrap; |
33 | align-items: center; | ||
16 | 34 | ||
17 | .register-form { | 35 | .skip-step { |
18 | max-width: 600px; | 36 | @include margin-right(30px); |
19 | align-self: center; | 37 | |
38 | display: inline-block; | ||
20 | } | 39 | } |
21 | 40 | ||
22 | .register-form, | 41 | .skip-step-description { |
23 | .instance-information { | 42 | margin-top: 5px; |
24 | width: 100%; | 43 | font-size: 14px; |
25 | } | 44 | } |
26 | 45 | ||
27 | .instance-information { | 46 | .underline-orange { |
28 | margin-bottom: 15px; | 47 | color: pvar(--mainForegroundColor); |
48 | |||
49 | &:hover { | ||
50 | opacity: 0.8; | ||
51 | } | ||
29 | } | 52 | } |
30 | } | ||
31 | 53 | ||
32 | input:not([type=submit]) { | 54 | button, |
33 | @include peertube-input-text(100%); | 55 | .skip-step { |
34 | display: block; | 56 | margin-top: 20px; |
57 | margin-bottom: 20px; | ||
58 | } | ||
35 | 59 | ||
36 | &#username, | 60 | .skip-step, |
37 | &#name { | 61 | button[cdkStepperNext] { |
38 | width: auto !important; | 62 | @include margin-left(auto); |
39 | flex-grow: 1; | 63 | } |
64 | |||
65 | .skip-step + button[cdkStepperNext] { | ||
66 | @include margin-left(0); | ||
40 | } | 67 | } |
41 | } | 68 | } |
42 | 69 | ||
43 | input[type=submit], | ||
44 | button { | 70 | button { |
45 | @include peertube-button; | 71 | @include peertube-button-big; |
46 | 72 | ||
47 | &[cdkStepperNext] { | 73 | &[cdkStepperNext] { |
48 | @include orange-button; | 74 | @include orange-button; |
49 | |||
50 | // Chrome does not support inline-end | ||
51 | float: right; | ||
52 | float: inline-end; | ||
53 | } | 75 | } |
54 | 76 | ||
55 | &[cdkStepperPrevious] { | 77 | &[cdkStepperPrevious] { |
56 | @include grey-button; | 78 | @include grey-button; |
57 | |||
58 | // Chrome does not support inline-start | ||
59 | float: left; | ||
60 | float: inline-start; | ||
61 | } | 79 | } |
62 | } | 80 | } |
63 | 81 | ||
64 | .name-information { | ||
65 | margin-top: 10px; | ||
66 | } | ||
67 | |||
68 | .done-loader { | 82 | .done-loader { |
69 | display: flex; | 83 | display: flex; |
70 | justify-content: center; | 84 | justify-content: center; |
@@ -73,13 +87,16 @@ button { | |||
73 | 87 | ||
74 | my-loader { | 88 | my-loader { |
75 | margin-bottom: 20px; | 89 | margin-bottom: 20px; |
90 | } | ||
91 | } | ||
76 | 92 | ||
77 | ::ng-deep .loader div { | 93 | @media screen and (max-width: $small-view) { |
78 | border-color: pvar(--mainColor) transparent transparent transparent; | 94 | .step-buttons { |
79 | } | 95 | justify-content: space-between; |
80 | 96 | ||
81 | + div { | 97 | .skip-step, |
82 | font-size: 15px; | 98 | button[cdkStepperNext] { |
99 | @include margin-left(0); | ||
83 | } | 100 | } |
84 | } | 101 | } |
85 | } | 102 | } |
diff --git a/client/src/app/+signup/+register/register.component.ts b/client/src/app/+signup/+register/register.component.ts index b4a7c0d0e..396b27e5a 100644 --- a/client/src/app/+signup/+register/register.component.ts +++ b/client/src/app/+signup/+register/register.component.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { CdkStep } from '@angular/cdk/stepper' |
2 | import { Component, OnInit, ViewChild } from '@angular/core' | ||
2 | import { FormGroup } from '@angular/forms' | 3 | import { FormGroup } from '@angular/forms' |
3 | import { ActivatedRoute } from '@angular/router' | 4 | import { ActivatedRoute } from '@angular/router' |
4 | import { AuthService } from '@app/core' | 5 | import { AuthService } from '@app/core' |
@@ -15,13 +16,15 @@ import { ServerConfig } from '@shared/models/server' | |||
15 | styleUrls: [ './register.component.scss' ] | 16 | styleUrls: [ './register.component.scss' ] |
16 | }) | 17 | }) |
17 | export class RegisterComponent implements OnInit { | 18 | export class RegisterComponent implements OnInit { |
19 | @ViewChild('lastStep') lastStep: CdkStep | ||
20 | |||
18 | accordion: NgbAccordion | 21 | accordion: NgbAccordion |
19 | info: string = null | 22 | |
20 | error: string = null | 23 | signupError: string |
21 | success: string = null | 24 | signupSuccess = false |
22 | signupDone = false | ||
23 | 25 | ||
24 | videoUploadDisabled: boolean | 26 | videoUploadDisabled: boolean |
27 | videoQuota: number | ||
25 | 28 | ||
26 | formStepTerms: FormGroup | 29 | formStepTerms: FormGroup |
27 | formStepUser: FormGroup | 30 | formStepUser: FormGroup |
@@ -39,8 +42,8 @@ export class RegisterComponent implements OnInit { | |||
39 | moderation: false | 42 | moderation: false |
40 | } | 43 | } |
41 | 44 | ||
42 | defaultPreviousStepButtonLabel = $localize`:Button on the registration form to go to the previous step:Back` | 45 | defaultPreviousStepButtonLabel = $localize`:Button on the registration form to go to the previous step:Go to the previous step` |
43 | defaultNextStepButtonLabel = $localize`:Button on the registration form to go to the previous step:Next` | 46 | defaultNextStepButtonLabel = $localize`:Button on the registration form to go to the previous step:Go to the next step` |
44 | stepUserButtonLabel = this.defaultNextStepButtonLabel | 47 | stepUserButtonLabel = this.defaultNextStepButtonLabel |
45 | 48 | ||
46 | signupDisabled = false | 49 | signupDisabled = false |
@@ -62,7 +65,11 @@ export class RegisterComponent implements OnInit { | |||
62 | return this.serverConfig.signup.minimumAge | 65 | return this.serverConfig.signup.minimumAge |
63 | } | 66 | } |
64 | 67 | ||
65 | ngOnInit (): void { | 68 | get instanceName () { |
69 | return this.serverConfig.instance.name | ||
70 | } | ||
71 | |||
72 | ngOnInit () { | ||
66 | this.serverConfig = this.route.snapshot.data.serverConfig | 73 | this.serverConfig = this.route.snapshot.data.serverConfig |
67 | 74 | ||
68 | if (this.serverConfig.signup.allowed === false || this.serverConfig.signup.allowedForCurrentIP === false) { | 75 | if (this.serverConfig.signup.allowed === false || this.serverConfig.signup.allowedForCurrentIP === false) { |
@@ -70,7 +77,9 @@ export class RegisterComponent implements OnInit { | |||
70 | return | 77 | return |
71 | } | 78 | } |
72 | 79 | ||
73 | this.videoUploadDisabled = this.serverConfig.user.videoQuota === 0 | 80 | this.videoQuota = this.serverConfig.user.videoQuota |
81 | this.videoUploadDisabled = this.videoQuota === 0 | ||
82 | |||
74 | this.stepUserButtonLabel = this.videoUploadDisabled | 83 | this.stepUserButtonLabel = this.videoUploadDisabled |
75 | ? $localize`:Button on the registration form to finalize the account and channel creation:Signup` | 84 | ? $localize`:Button on the registration form to finalize the account and channel creation:Signup` |
76 | : this.defaultNextStepButtonLabel | 85 | : this.defaultNextStepButtonLabel |
@@ -120,21 +129,31 @@ export class RegisterComponent implements OnInit { | |||
120 | this.aboutHtml = instanceAboutAccordion.aboutHtml | 129 | this.aboutHtml = instanceAboutAccordion.aboutHtml |
121 | } | 130 | } |
122 | 131 | ||
132 | skipChannelCreation () { | ||
133 | this.formStepChannel.reset() | ||
134 | this.lastStep.select() | ||
135 | this.signup() | ||
136 | } | ||
137 | |||
123 | async signup () { | 138 | async signup () { |
124 | this.error = null | 139 | this.signupError = undefined |
125 | 140 | ||
126 | const body: UserRegister = await this.hooks.wrapObject( | 141 | const body: UserRegister = await this.hooks.wrapObject( |
127 | Object.assign(this.formStepUser.value, { channel: this.videoUploadDisabled ? undefined : this.formStepChannel.value }), | 142 | { |
143 | ...this.formStepUser.value, | ||
144 | |||
145 | channel: this.formStepChannel?.value?.name | ||
146 | ? this.formStepChannel.value | ||
147 | : undefined | ||
148 | }, | ||
128 | 'signup', | 149 | 'signup', |
129 | 'filter:api.signup.registration.create.params' | 150 | 'filter:api.signup.registration.create.params' |
130 | ) | 151 | ) |
131 | 152 | ||
132 | this.userSignupService.signup(body).subscribe({ | 153 | this.userSignupService.signup(body).subscribe({ |
133 | next: () => { | 154 | next: () => { |
134 | this.signupDone = true | ||
135 | |||
136 | if (this.requiresEmailVerification) { | 155 | if (this.requiresEmailVerification) { |
137 | this.info = $localize`Now please check your emails to verify your account and complete signup.` | 156 | this.signupSuccess = true |
138 | return | 157 | return |
139 | } | 158 | } |
140 | 159 | ||
@@ -142,17 +161,17 @@ export class RegisterComponent implements OnInit { | |||
142 | this.authService.login(body.username, body.password) | 161 | this.authService.login(body.username, body.password) |
143 | .subscribe({ | 162 | .subscribe({ |
144 | next: () => { | 163 | next: () => { |
145 | this.success = $localize`You are now logged in as ${body.username}!` | 164 | this.signupSuccess = true |
146 | }, | 165 | }, |
147 | 166 | ||
148 | error: err => { | 167 | error: err => { |
149 | this.error = err.message | 168 | this.signupError = err.message |
150 | } | 169 | } |
151 | }) | 170 | }) |
152 | }, | 171 | }, |
153 | 172 | ||
154 | error: err => { | 173 | error: err => { |
155 | this.error = err.message | 174 | this.signupError = err.message |
156 | } | 175 | } |
157 | }) | 176 | }) |
158 | } | 177 | } |
diff --git a/client/src/app/+signup/+register/register.module.ts b/client/src/app/+signup/+register/register.module.ts index 52cdb33bc..684aae2e9 100644 --- a/client/src/app/+signup/+register/register.module.ts +++ b/client/src/app/+signup/+register/register.module.ts | |||
@@ -2,15 +2,15 @@ import { CdkStepperModule } from '@angular/cdk/stepper' | |||
2 | import { NgModule } from '@angular/core' | 2 | import { NgModule } from '@angular/core' |
3 | import { SharedSignupModule } from '@app/+signup/shared/shared-signup.module' | 3 | import { SharedSignupModule } from '@app/+signup/shared/shared-signup.module' |
4 | import { SharedInstanceModule } from '@app/shared/shared-instance' | 4 | import { SharedInstanceModule } from '@app/shared/shared-instance' |
5 | import { SharedMainModule } from '@app/shared/shared-main' | ||
5 | import { CustomStepperComponent } from './custom-stepper.component' | 6 | import { CustomStepperComponent } from './custom-stepper.component' |
6 | import { RegisterRoutingModule } from './register-routing.module' | 7 | import { RegisterRoutingModule } from './register-routing.module' |
7 | import { RegisterStepChannelComponent } from './register-step-channel.component' | ||
8 | import { RegisterStepTermsComponent } from './register-step-terms.component' | ||
9 | import { RegisterStepUserComponent } from './register-step-user.component' | ||
10 | import { RegisterComponent } from './register.component' | 8 | import { RegisterComponent } from './register.component' |
9 | import { RegisterStepAboutComponent, RegisterStepChannelComponent, RegisterStepTermsComponent, RegisterStepUserComponent } from './steps' | ||
11 | 10 | ||
12 | @NgModule({ | 11 | @NgModule({ |
13 | imports: [ | 12 | imports: [ |
13 | SharedMainModule, | ||
14 | RegisterRoutingModule, | 14 | RegisterRoutingModule, |
15 | 15 | ||
16 | CdkStepperModule, | 16 | CdkStepperModule, |
@@ -25,7 +25,8 @@ import { RegisterComponent } from './register.component' | |||
25 | CustomStepperComponent, | 25 | CustomStepperComponent, |
26 | RegisterStepChannelComponent, | 26 | RegisterStepChannelComponent, |
27 | RegisterStepTermsComponent, | 27 | RegisterStepTermsComponent, |
28 | RegisterStepUserComponent | 28 | RegisterStepUserComponent, |
29 | RegisterStepAboutComponent | ||
29 | ], | 30 | ], |
30 | 31 | ||
31 | exports: [ | 32 | exports: [ |
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 @@ | |||
1 | export * from './register-step-about.component' | ||
2 | export * from './register-step-channel.component' | ||
3 | export * from './register-step-terms.component' | ||
4 | export * 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 | |||
4 | h3 { | ||
5 | font-weight: $font-bold; | ||
6 | font-size: 24px; | ||
7 | } | ||
8 | |||
9 | h4 { | ||
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 @@ | |||
1 | import { Component, Input } from '@angular/core' | ||
2 | import { 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 | }) | ||
9 | export 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/register-step-channel.component.ts b/client/src/app/+signup/+register/steps/register-step-channel.component.ts index 1bc0ccfd3..c10b568ba 100644 --- a/client/src/app/+signup/+register/register-step-channel.component.ts +++ b/client/src/app/+signup/+register/steps/register-step-channel.component.ts | |||
@@ -9,10 +9,13 @@ import { UserSignupService } from '@app/shared/shared-users' | |||
9 | @Component({ | 9 | @Component({ |
10 | selector: 'my-register-step-channel', | 10 | selector: 'my-register-step-channel', |
11 | templateUrl: './register-step-channel.component.html', | 11 | templateUrl: './register-step-channel.component.html', |
12 | styleUrls: [ './register.component.scss' ] | 12 | styleUrls: [ './step.component.scss' ] |
13 | }) | 13 | }) |
14 | export class RegisterStepChannelComponent extends FormReactive implements OnInit { | 14 | export class RegisterStepChannelComponent extends FormReactive implements OnInit { |
15 | @Input() username: string | 15 | @Input() username: string |
16 | @Input() instanceName: string | ||
17 | @Input() videoQuota: number | ||
18 | |||
16 | @Output() formBuilt = new EventEmitter<FormGroup>() | 19 | @Output() formBuilt = new EventEmitter<FormGroup>() |
17 | 20 | ||
18 | constructor ( | 21 | constructor ( |
diff --git a/client/src/app/+signup/+register/register-step-terms.component.html b/client/src/app/+signup/+register/steps/register-step-terms.component.html index 717a289e6..f54ca77e2 100644 --- a/client/src/app/+signup/+register/register-step-terms.component.html +++ b/client/src/app/+signup/+register/steps/register-step-terms.component.html | |||
@@ -4,15 +4,13 @@ | |||
4 | <ng-template ptTemplate="label"> | 4 | <ng-template ptTemplate="label"> |
5 | <ng-container i18n> | 5 | <ng-container i18n> |
6 | I am at least {{ minimumAge }} years old and agree | 6 | I am at least {{ minimumAge }} years old and agree |
7 | to the <a class="terms-anchor" (click)="onTermsClick($event)" href='#'>Terms</a> | 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> | 8 | <ng-container *ngIf="hasCodeOfConduct"> and to the <a (click)="onCodeOfConductClick($event)" href='#'>Code of Conduct</a></ng-container> |
9 | of this instance | 9 | of this instance |
10 | </ng-container> | 10 | </ng-container> |
11 | </ng-template> | 11 | </ng-template> |
12 | </my-peertube-checkbox> | 12 | </my-peertube-checkbox> |
13 | 13 | ||
14 | <div *ngIf="formErrors.terms" class="form-error"> | 14 | <div *ngIf="formErrors.terms" class="form-error">{{ formErrors.terms }}</div> |
15 | {{ formErrors.terms }} | ||
16 | </div> | ||
17 | </div> | 15 | </div> |
18 | </form> | 16 | </form> |
diff --git a/client/src/app/+signup/+register/register-step-terms.component.ts b/client/src/app/+signup/+register/steps/register-step-terms.component.ts index 20c1ae1c4..87d16696e 100644 --- a/client/src/app/+signup/+register/register-step-terms.component.ts +++ b/client/src/app/+signup/+register/steps/register-step-terms.component.ts | |||
@@ -8,7 +8,7 @@ import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' | |||
8 | @Component({ | 8 | @Component({ |
9 | selector: 'my-register-step-terms', | 9 | selector: 'my-register-step-terms', |
10 | templateUrl: './register-step-terms.component.html', | 10 | templateUrl: './register-step-terms.component.html', |
11 | styleUrls: [ './register.component.scss' ] | 11 | styleUrls: [ './step.component.scss' ] |
12 | }) | 12 | }) |
13 | export class RegisterStepTermsComponent extends FormReactive implements OnInit { | 13 | export class RegisterStepTermsComponent extends FormReactive implements OnInit { |
14 | @Input() hasCodeOfConduct = false | 14 | @Input() hasCodeOfConduct = false |
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/register-step-user.component.ts b/client/src/app/+signup/+register/steps/register-step-user.component.ts index 92ddfca2e..b89e38a28 100644 --- a/client/src/app/+signup/+register/register-step-user.component.ts +++ b/client/src/app/+signup/+register/steps/register-step-user.component.ts | |||
@@ -14,10 +14,11 @@ import { UserSignupService } from '@app/shared/shared-users' | |||
14 | @Component({ | 14 | @Component({ |
15 | selector: 'my-register-step-user', | 15 | selector: 'my-register-step-user', |
16 | templateUrl: './register-step-user.component.html', | 16 | templateUrl: './register-step-user.component.html', |
17 | styleUrls: [ './register.component.scss' ] | 17 | styleUrls: [ './step.component.scss' ] |
18 | }) | 18 | }) |
19 | export class RegisterStepUserComponent extends FormReactive implements OnInit { | 19 | export class RegisterStepUserComponent extends FormReactive implements OnInit { |
20 | @Input() videoUploadDisabled = false | 20 | @Input() videoUploadDisabled = false |
21 | @Input() requiresEmailVerification = false | ||
21 | 22 | ||
22 | @Output() formBuilt = new EventEmitter<FormGroup>() | 23 | @Output() formBuilt = new EventEmitter<FormGroup>() |
23 | 24 | ||
@@ -49,6 +50,10 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit { | |||
49 | .subscribe(([ oldValue, newValue ]) => this.onDisplayNameChange(oldValue, newValue)) | 50 | .subscribe(([ oldValue, newValue ]) => this.onDisplayNameChange(oldValue, newValue)) |
50 | } | 51 | } |
51 | 52 | ||
53 | getMinPasswordLengthMessage () { | ||
54 | return USER_PASSWORD_VALIDATOR.MESSAGES.minlength | ||
55 | } | ||
56 | |||
52 | private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) { | 57 | private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) { |
53 | const username = this.form.value['username'] || '' | 58 | const username = this.form.value['username'] || '' |
54 | 59 | ||
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 | |||
4 | input: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 | |||
15 | input[type=submit], | ||
16 | button { | ||
17 | @include peertube-button; | ||
18 | } | ||
19 | |||
20 | label { | ||
21 | font-size: 18px; | ||
22 | margin-bottom: 5px; | ||
23 | } | ||
24 | |||
25 | .row { | ||
26 | margin-bottom: 30px; | ||
27 | } | ||
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 index 47519c943..327e23f3f 100644 --- 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 | |||
@@ -3,7 +3,7 @@ | |||
3 | Verify account email confirmation | 3 | Verify account email confirmation |
4 | </div> | 4 | </div> |
5 | 5 | ||
6 | <my-signup-success i18n *ngIf="!isPendingEmail && success" message="Your email has been verified and you may now login."> | 6 | <my-signup-success i18n *ngIf="!isPendingEmail && success" [requiresEmailVerification]="false"> |
7 | </my-signup-success> | 7 | </my-signup-success> |
8 | 8 | ||
9 | <div i18n class="alert alert-success" *ngIf="isPendingEmail && success"> | 9 | <div i18n class="alert alert-success" *ngIf="isPendingEmail && success"> |
diff --git a/client/src/app/+signup/shared/shared-signup.module.ts b/client/src/app/+signup/shared/shared-signup.module.ts index f8b224c71..0aa08f3e2 100644 --- a/client/src/app/+signup/shared/shared-signup.module.ts +++ b/client/src/app/+signup/shared/shared-signup.module.ts | |||
@@ -3,6 +3,8 @@ import { SharedFormModule } from '@app/shared/shared-forms' | |||
3 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' | 3 | import { SharedGlobalIconModule } from '@app/shared/shared-icons' |
4 | import { SharedMainModule } from '@app/shared/shared-main' | 4 | import { SharedMainModule } from '@app/shared/shared-main' |
5 | import { SharedUsersModule } from '@app/shared/shared-users' | 5 | import { SharedUsersModule } from '@app/shared/shared-users' |
6 | import { SignupMascotComponent } from './signup-mascot.component' | ||
7 | import { SignupStepTitleComponent } from './signup-step-title.component' | ||
6 | import { SignupSuccessComponent } from './signup-success.component' | 8 | import { SignupSuccessComponent } from './signup-success.component' |
7 | 9 | ||
8 | @NgModule({ | 10 | @NgModule({ |
@@ -14,7 +16,9 @@ import { SignupSuccessComponent } from './signup-success.component' | |||
14 | ], | 16 | ], |
15 | 17 | ||
16 | declarations: [ | 18 | declarations: [ |
17 | SignupSuccessComponent | 19 | SignupSuccessComponent, |
20 | SignupStepTitleComponent, | ||
21 | SignupMascotComponent | ||
18 | ], | 22 | ], |
19 | 23 | ||
20 | exports: [ | 24 | exports: [ |
@@ -22,7 +26,9 @@ import { SignupSuccessComponent } from './signup-success.component' | |||
22 | SharedFormModule, | 26 | SharedFormModule, |
23 | SharedGlobalIconModule, | 27 | SharedGlobalIconModule, |
24 | 28 | ||
25 | SignupSuccessComponent | 29 | SignupSuccessComponent, |
30 | SignupStepTitleComponent, | ||
31 | SignupMascotComponent | ||
26 | ], | 32 | ], |
27 | 33 | ||
28 | providers: [ | 34 | providers: [ |
diff --git a/client/src/app/+signup/shared/signup-mascot.component.scss b/client/src/app/+signup/shared/signup-mascot.component.scss new file mode 100644 index 000000000..5eebfb014 --- /dev/null +++ b/client/src/app/+signup/shared/signup-mascot.component.scss | |||
@@ -0,0 +1,11 @@ | |||
1 | @use '_variables' as *; | ||
2 | @use '_mixins' as *; | ||
3 | |||
4 | .root { | ||
5 | display: inline-block; | ||
6 | width: 270px; | ||
7 | } | ||
8 | |||
9 | div ::ng-deep svg { | ||
10 | color: pvar(--mainColor); | ||
11 | } | ||
diff --git a/client/src/app/+signup/shared/signup-mascot.component.ts b/client/src/app/+signup/shared/signup-mascot.component.ts new file mode 100644 index 000000000..a96ccffee --- /dev/null +++ b/client/src/app/+signup/shared/signup-mascot.component.ts | |||
@@ -0,0 +1,29 @@ | |||
1 | import { Component, Input } from '@angular/core' | ||
2 | import { DomSanitizer } from '@angular/platform-browser' | ||
3 | |||
4 | const images = { | ||
5 | about: require('!!raw-loader?!../../../assets/images/mascot/register/about.svg').default, | ||
6 | terms: require('!!raw-loader?!../../../assets/images/mascot/register/terms.svg').default, | ||
7 | success: require('!!raw-loader?!../../../assets/images/mascot/register/success.svg').default, | ||
8 | channel: require('!!raw-loader?!../../../assets/images/mascot/register/channel.svg').default, | ||
9 | account: require('!!raw-loader?!../../../assets/images/mascot/register/account.svg').default | ||
10 | } | ||
11 | |||
12 | export type MascotImageName = keyof typeof images | ||
13 | |||
14 | @Component({ | ||
15 | selector: 'my-signup-mascot', | ||
16 | styleUrls: [ './signup-mascot.component.scss' ], | ||
17 | template: `<div class="root" [innerHTML]="html"></div>` | ||
18 | }) | ||
19 | export class SignupMascotComponent { | ||
20 | @Input() imageName: MascotImageName | ||
21 | |||
22 | constructor (private sanitize: DomSanitizer) { | ||
23 | |||
24 | } | ||
25 | |||
26 | get html () { | ||
27 | return this.sanitize.bypassSecurityTrustHtml(images[this.imageName]) | ||
28 | } | ||
29 | } | ||
diff --git a/client/src/app/+signup/shared/signup-step-title.component.html b/client/src/app/+signup/shared/signup-step-title.component.html new file mode 100644 index 000000000..9cf4c4826 --- /dev/null +++ b/client/src/app/+signup/shared/signup-step-title.component.html | |||
@@ -0,0 +1,9 @@ | |||
1 | <div class="step-content-title"> | ||
2 | <my-signup-mascot [imageName]="mascotImageName"></my-signup-mascot> | ||
3 | |||
4 | <h2> | ||
5 | <ng-content></ng-content> | ||
6 | </h2> | ||
7 | |||
8 | <div class="step-content-title-separator"></div> | ||
9 | </div> | ||
diff --git a/client/src/app/+signup/shared/signup-step-title.component.scss b/client/src/app/+signup/shared/signup-step-title.component.scss new file mode 100644 index 000000000..1e0cb2440 --- /dev/null +++ b/client/src/app/+signup/shared/signup-step-title.component.scss | |||
@@ -0,0 +1,23 @@ | |||
1 | @use '_variables' as *; | ||
2 | @use '_mixins' as *; | ||
3 | |||
4 | .step-content-title { | ||
5 | text-align: center; | ||
6 | margin: auto; | ||
7 | margin-bottom: 45px; | ||
8 | |||
9 | h2 { | ||
10 | font-size: 32px; | ||
11 | font-weight: normal; | ||
12 | max-width: 300px; | ||
13 | margin: 15px auto 0; | ||
14 | } | ||
15 | } | ||
16 | |||
17 | .step-content-title-separator { | ||
18 | height: 6px; | ||
19 | width: 60px; | ||
20 | border-radius: 4px; | ||
21 | background-color: pvar(--mainColor); | ||
22 | margin: 5px auto 0; | ||
23 | } | ||
diff --git a/client/src/app/+signup/shared/signup-step-title.component.ts b/client/src/app/+signup/shared/signup-step-title.component.ts new file mode 100644 index 000000000..9664eb7f3 --- /dev/null +++ b/client/src/app/+signup/shared/signup-step-title.component.ts | |||
@@ -0,0 +1,12 @@ | |||
1 | import { Component, Input } from '@angular/core' | ||
2 | import { MascotImageName } from './signup-mascot.component' | ||
3 | |||
4 | @Component({ | ||
5 | selector: 'my-signup-step-title', | ||
6 | templateUrl: './signup-step-title.component.html', | ||
7 | styleUrls: [ './signup-step-title.component.scss' ] | ||
8 | }) | ||
9 | export class SignupStepTitleComponent { | ||
10 | @Input() mascotImageName: MascotImageName | ||
11 | |||
12 | } | ||
diff --git a/client/src/app/+signup/shared/signup-success.component.html b/client/src/app/+signup/shared/signup-success.component.html index d66e8b568..c14889c72 100644 --- a/client/src/app/+signup/shared/signup-success.component.html +++ b/client/src/app/+signup/shared/signup-success.component.html | |||
@@ -1,20 +1,22 @@ | |||
1 | <!-- Thanks: Amit Singh Sansoya from https://codepen.io/amit3200/pen/zWMJOO --> | 1 | <my-signup-step-title mascotImageName="success" i18n> |
2 | <strong>Welcome</strong> | ||
3 | <div>on {{ instanceName }}</div> | ||
4 | </my-signup-step-title> | ||
2 | 5 | ||
3 | <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 130.2 130.2"> | 6 | <div class="alert pt-alert-primary"> |
4 | <circle class="path circle" fill="none" stroke="#73AF55" stroke-width="6" stroke-miterlimit="10" cx="65.1" cy="65.1" r="62.1"/> | 7 | <p i18n>Your account has been created!</p> |
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 | ||
8 | <p i18n class="bottom-message">Welcome to PeerTube!</p> | 9 | <p i18n *ngIf="requiresEmailVerification"> |
9 | 10 | <strong>Check your emails</strong> to validate your account and complete your inscription. | |
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 have a look at the <a href="https://docs.joinpeertube.org/use-setup-account" target="_blank" rel="noopener noreferrer">documentation</a>. | ||
15 | </p> | 11 | </p> |
16 | 12 | ||
17 | <p i18n> | 13 | <ng-container *ngIf="!requiresEmailVerification"> |
18 | To help moderators and other users to know <strong>who you are</strong>, don't forget to <a routerLink="/my-account/settings">set up your account profile</a> by adding an <strong>avatar</strong> and a <strong>description</strong>. | 14 | <p i18n> |
19 | </p> | 15 | If you need help to use PeerTube, you can have a look at the <a class="link-orange" href="https://docs.joinpeertube.org/use-setup-account" target="_blank" rel="noopener noreferrer">documentation</a>. |
16 | </p> | ||
17 | |||
18 | <p i18n> | ||
19 | To help moderators and other users to know <strong>who you are</strong>, don't forget to <a class="link-orange" routerLink="/my-account/settings">set up your account profile</a> by adding an <strong>avatar</strong> and a <strong>description</strong>. | ||
20 | </p> | ||
21 | </ng-container> | ||
20 | </div> | 22 | </div> |
diff --git a/client/src/app/+signup/shared/signup-success.component.scss b/client/src/app/+signup/shared/signup-success.component.scss index b302366e2..918349ba0 100644 --- a/client/src/app/+signup/shared/signup-success.component.scss +++ b/client/src/app/+signup/shared/signup-success.component.scss | |||
@@ -1,54 +1,6 @@ | |||
1 | svg { | ||
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 | animation: dash .9s ease-in-out; | ||
13 | } | ||
14 | |||
15 | &.line { | ||
16 | stroke-dashoffset: 1000; | ||
17 | animation: dash .9s .35s ease-in-out forwards; | ||
18 | } | ||
19 | |||
20 | &.check { | ||
21 | stroke-dashoffset: -100; | ||
22 | animation: dash-check .9s .35s ease-in-out forwards; | ||
23 | } | ||
24 | } | ||
25 | |||
26 | .bottom-message { | ||
27 | text-align: center; | ||
28 | margin: 20px 0 60px; | ||
29 | font-size: 1.25em; | ||
30 | color: #73AF55; | ||
31 | } | ||
32 | |||
33 | .alert { | 1 | .alert { |
34 | font-size: 15px; | 2 | font-size: 18px; |
3 | max-width: 900px; | ||
35 | text-align: center; | 4 | text-align: center; |
36 | } | 5 | margin: auto; |
37 | |||
38 | @keyframes dash { | ||
39 | 0% { | ||
40 | stroke-dashoffset: 1000; | ||
41 | } | ||
42 | 100% { | ||
43 | stroke-dashoffset: 0; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | @keyframes dash-check { | ||
48 | 0% { | ||
49 | stroke-dashoffset: -100; | ||
50 | } | ||
51 | 100% { | ||
52 | stroke-dashoffset: 900; | ||
53 | } | ||
54 | } | 6 | } |
diff --git a/client/src/app/+signup/shared/signup-success.component.ts b/client/src/app/+signup/shared/signup-success.component.ts index 19fb5922a..a03f3819d 100644 --- a/client/src/app/+signup/shared/signup-success.component.ts +++ b/client/src/app/+signup/shared/signup-success.component.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Component, Input } from '@angular/core' | 1 | import { Component, Input } from '@angular/core' |
2 | import { ServerService } from '@app/core' | ||
2 | 3 | ||
3 | @Component({ | 4 | @Component({ |
4 | selector: 'my-signup-success', | 5 | selector: 'my-signup-success', |
@@ -6,5 +7,13 @@ import { Component, Input } from '@angular/core' | |||
6 | styleUrls: [ './signup-success.component.scss' ] | 7 | styleUrls: [ './signup-success.component.scss' ] |
7 | }) | 8 | }) |
8 | export class SignupSuccessComponent { | 9 | export class SignupSuccessComponent { |
9 | @Input() message: string | 10 | @Input() requiresEmailVerification: boolean |
11 | |||
12 | constructor (private serverService: ServerService) { | ||
13 | |||
14 | } | ||
15 | |||
16 | get instanceName () { | ||
17 | return this.serverService.getHTMLConfig().instance.name | ||
18 | } | ||
10 | } | 19 | } |
diff --git a/client/src/app/shared/form-validators/user-validators.ts b/client/src/app/shared/form-validators/user-validators.ts index 6d0dea64e..3262853d8 100644 --- a/client/src/app/shared/form-validators/user-validators.ts +++ b/client/src/app/shared/form-validators/user-validators.ts | |||
@@ -61,7 +61,7 @@ export const USER_EXISTING_PASSWORD_VALIDATOR: BuildFormValidator = { | |||
61 | } | 61 | } |
62 | } | 62 | } |
63 | 63 | ||
64 | export const USER_PASSWORD_VALIDATOR: BuildFormValidator = { | 64 | export const USER_PASSWORD_VALIDATOR = { |
65 | VALIDATORS: [ | 65 | VALIDATORS: [ |
66 | Validators.required, | 66 | Validators.required, |
67 | Validators.minLength(6), | 67 | Validators.minLength(6), |
diff --git a/client/src/app/shared/shared-instance/instance-about-accordion.component.html b/client/src/app/shared/shared-instance/instance-about-accordion.component.html index 73e511d1c..466d73ca4 100644 --- a/client/src/app/shared/shared-instance/instance-about-accordion.component.html +++ b/client/src/app/shared/shared-instance/instance-about-accordion.component.html | |||
@@ -1,6 +1,6 @@ | |||
1 | <h2 class="instance-name">{{ about?.instance.name }}</h2> | 1 | <h2 *ngIf="displayInstanceName" class="instance-name">{{ about?.instance.name }}</h2> |
2 | 2 | ||
3 | <div class="instance-short-description">{{ about?.instance.shortDescription }}</div> | 3 | <div *ngIf="displayInstanceShortDescription" class="instance-short-description">{{ about?.instance.shortDescription }}</div> |
4 | 4 | ||
5 | <ngb-accordion #accordion="ngbAccordion" [closeOthers]="true"> | 5 | <ngb-accordion #accordion="ngbAccordion" [closeOthers]="true"> |
6 | <ngb-panel *ngIf="panels.features" id="instance-features" i18n-title title="Features found on this instance"> | 6 | <ngb-panel *ngIf="panels.features" id="instance-features" i18n-title title="Features found on this instance"> |
@@ -32,7 +32,7 @@ | |||
32 | </ng-template> | 32 | </ng-template> |
33 | </ngb-panel> | 33 | </ngb-panel> |
34 | 34 | ||
35 | <ngb-panel *ngIf="termsPanel" id="terms" i18n-title title="Terms"> | 35 | <ngb-panel *ngIf="termsPanel" id="terms" [title]="getTermsTitle()"> |
36 | <ng-template ngbPanelContent> | 36 | <ng-template ngbPanelContent> |
37 | <div class="block" [innerHTML]="aboutHtml.terms"></div> | 37 | <div class="block" [innerHTML]="aboutHtml.terms"></div> |
38 | </ng-template> | 38 | </ng-template> |
diff --git a/client/src/app/shared/shared-instance/instance-about-accordion.component.scss b/client/src/app/shared/shared-instance/instance-about-accordion.component.scss index 8e5dfb064..0da7aede9 100644 --- a/client/src/app/shared/shared-instance/instance-about-accordion.component.scss +++ b/client/src/app/shared/shared-instance/instance-about-accordion.component.scss | |||
@@ -8,8 +8,7 @@ | |||
8 | .instance-short-description { | 8 | .instance-short-description { |
9 | @include ellipsis-multiline(1rem, 3); | 9 | @include ellipsis-multiline(1rem, 3); |
10 | 10 | ||
11 | margin-top: 20px; | 11 | margin: 25px 0; |
12 | margin-bottom: 20px; | ||
13 | } | 12 | } |
14 | 13 | ||
15 | .block { | 14 | .block { |
diff --git a/client/src/app/shared/shared-instance/instance-about-accordion.component.ts b/client/src/app/shared/shared-instance/instance-about-accordion.component.ts index b9f57e2a4..e13703c03 100644 --- a/client/src/app/shared/shared-instance/instance-about-accordion.component.ts +++ b/client/src/app/shared/shared-instance/instance-about-accordion.component.ts | |||
@@ -15,6 +15,9 @@ export class InstanceAboutAccordionComponent implements OnInit { | |||
15 | 15 | ||
16 | @Output() init: EventEmitter<InstanceAboutAccordionComponent> = new EventEmitter<InstanceAboutAccordionComponent>() | 16 | @Output() init: EventEmitter<InstanceAboutAccordionComponent> = new EventEmitter<InstanceAboutAccordionComponent>() |
17 | 17 | ||
18 | @Input() displayInstanceName = true | ||
19 | @Input() displayInstanceShortDescription = true | ||
20 | |||
18 | @Input() pluginScope: PluginClientScope | 21 | @Input() pluginScope: PluginClientScope |
19 | @Input() pluginHook: ClientFilterHookName | 22 | @Input() pluginHook: ClientFilterHookName |
20 | 23 | ||
@@ -66,6 +69,10 @@ export class InstanceAboutAccordionComponent implements OnInit { | |||
66 | return !!(this.aboutHtml?.administrator || this.about?.instance.maintenanceLifetime || this.about?.instance.businessModel) | 69 | return !!(this.aboutHtml?.administrator || this.about?.instance.maintenanceLifetime || this.about?.instance.businessModel) |
67 | } | 70 | } |
68 | 71 | ||
72 | getTermsTitle () { | ||
73 | return $localize`Terms of ${this.about.instance.name}` | ||
74 | } | ||
75 | |||
69 | get moderationPanel () { | 76 | get moderationPanel () { |
70 | return this.panels.moderation && !!this.aboutHtml.moderationInformation | 77 | return this.panels.moderation && !!this.aboutHtml.moderationInformation |
71 | } | 78 | } |