mirror of https://github.com/Chocobozzz/PeerTube
Multi step registration
parent
e590b4a512
commit
1d5342abc4
|
@ -14,9 +14,6 @@
|
||||||
|
|
||||||
input {
|
input {
|
||||||
@include peertube-checkbox(1px);
|
@include peertube-checkbox(1px);
|
||||||
|
|
||||||
width: 10px;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
|
||||||
import { SortMeta } from 'primeng/api'
|
import { SortMeta } from 'primeng/api'
|
||||||
import { BytesPipe } from 'ngx-pipes'
|
import { BytesPipe } from 'ngx-pipes'
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
|
import { UserRegister } from '@shared/models/users/user-register.model'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
|
@ -64,7 +65,7 @@ export class UserService {
|
||||||
.pipe(catchError(err => this.restExtractor.handleError(err)))
|
.pipe(catchError(err => this.restExtractor.handleError(err)))
|
||||||
}
|
}
|
||||||
|
|
||||||
signup (userCreate: UserCreate) {
|
signup (userCreate: UserRegister) {
|
||||||
return this.authHttp.post(UserService.BASE_USERS_URL + 'register', userCreate)
|
return this.authHttp.post(UserService.BASE_USERS_URL + 'register', userCreate)
|
||||||
.pipe(
|
.pipe(
|
||||||
map(this.restExtractor.extractDataBool),
|
map(this.restExtractor.extractDataBool),
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<section class="container">
|
||||||
|
<header>
|
||||||
|
<ng-container *ngFor="let step of steps; let i = index; let isLast = last;">
|
||||||
|
<div
|
||||||
|
class="step-info" [ngClass]="{ active: selectedIndex === i, completed: isCompleted(step) }"
|
||||||
|
(click)="onClick(i)"
|
||||||
|
>
|
||||||
|
<div class="step-index">
|
||||||
|
<ng-container *ngIf="!isCompleted(step)">{{ i + 1 }}</ng-container>
|
||||||
|
<my-global-icon *ngIf="isCompleted(step)" iconName="tick"></my-global-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step-label">{{ step.label }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Do no display if this is the last child -->
|
||||||
|
<div *ngIf="!isLast" class="connector"></div>
|
||||||
|
</ng-container>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div [style.display]="selected ? 'block' : 'none'">
|
||||||
|
<ng-container [ngTemplateOutlet]="selected.content"></ng-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
|
@ -0,0 +1,66 @@
|
||||||
|
@import '_variables';
|
||||||
|
@import '_mixins';
|
||||||
|
|
||||||
|
$grey-color: #9CA3AB;
|
||||||
|
$index-block-height: 32px;
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
.step-info {
|
||||||
|
color: $grey-color;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: $index-block-height;
|
||||||
|
|
||||||
|
.step-index {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: $index-block-height;
|
||||||
|
height: $index-block-height;
|
||||||
|
border-radius: 100px;
|
||||||
|
border: 2px solid $grey-color;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
my-global-icon {
|
||||||
|
@include apply-svg-color(var(--mainBackgroundColor));
|
||||||
|
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-label {
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active,
|
||||||
|
&.completed {
|
||||||
|
.step-index {
|
||||||
|
border-color: var(--mainColor);
|
||||||
|
background-color: var(--mainColor);
|
||||||
|
color: var(--mainBackgroundColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-label {
|
||||||
|
color: var(--mainColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.completed {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.connector {
|
||||||
|
flex: auto;
|
||||||
|
margin: $index-block-height/2 10px 0 10px;
|
||||||
|
height: 2px;
|
||||||
|
background-color: $grey-color;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { CdkStep, CdkStepper } from '@angular/cdk/stepper'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-custom-stepper',
|
||||||
|
templateUrl: './custom-stepper.component.html',
|
||||||
|
styleUrls: [ './custom-stepper.component.scss' ],
|
||||||
|
providers: [ { provide: CdkStepper, useExisting: CustomStepperComponent } ]
|
||||||
|
})
|
||||||
|
export class CustomStepperComponent extends CdkStepper {
|
||||||
|
|
||||||
|
onClick (index: number): void {
|
||||||
|
this.selectedIndex = index
|
||||||
|
}
|
||||||
|
|
||||||
|
isCompleted (step: CdkStep) {
|
||||||
|
return step.stepControl && step.stepControl.dirty && step.stepControl.valid
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
<form role="form" [formGroup]="form">
|
||||||
|
|
||||||
|
<div class="channel-explanations">
|
||||||
|
<p i18n>
|
||||||
|
A channel is an entity in which you upload your videos. Creating several of them helps you to organize and separate your content.<br />
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Other users can decide to subscribe any channel they want, to be notified when you publish a new video.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name" i18n>Channel name</label>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
type="text" id="name" i18n-placeholder placeholder="Example: my_super_channel"
|
||||||
|
formControlName="name" [ngClass]="{ 'input-error': formErrors['name'] }"
|
||||||
|
>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span class="input-group-text">@{{ instanceHost }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="formErrors.name" class="form-error">
|
||||||
|
{{ formErrors.name }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="isSameThanUsername()" class="form-error" i18n>
|
||||||
|
Channel name cannot be the same than your account name. You can click on the first step to update your account name.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="displayName" i18n>Channel display name</label>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
type="text" id="displayName"
|
||||||
|
formControlName="displayName" [ngClass]="{ 'input-error': formErrors['displayName'] }"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="formErrors.displayName" class="form-error">
|
||||||
|
{{ formErrors.displayName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
|
||||||
|
import { AuthService } from '@app/core'
|
||||||
|
import { FormReactive, VideoChannelValidatorsService } from '../shared'
|
||||||
|
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
||||||
|
import { FormGroup } from '@angular/forms'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-signup-step-channel',
|
||||||
|
templateUrl: './signup-step-channel.component.html',
|
||||||
|
styleUrls: [ './signup.component.scss' ]
|
||||||
|
})
|
||||||
|
export class SignupStepChannelComponent extends FormReactive implements OnInit {
|
||||||
|
@Input() username: string
|
||||||
|
@Output() formBuilt = new EventEmitter<FormGroup>()
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
protected formValidatorService: FormValidatorService,
|
||||||
|
private authService: AuthService,
|
||||||
|
private videoChannelValidatorsService: VideoChannelValidatorsService
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
get instanceHost () {
|
||||||
|
return window.location.host
|
||||||
|
}
|
||||||
|
|
||||||
|
isSameThanUsername () {
|
||||||
|
return this.username && this.username === this.form.value['name']
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
this.buildForm({
|
||||||
|
name: this.videoChannelValidatorsService.VIDEO_CHANNEL_NAME,
|
||||||
|
displayName: this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => this.formBuilt.emit(this.form))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
<form role="form" [formGroup]="form">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username" i18n>Username</label>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
type="text" id="username" i18n-placeholder placeholder="Example: jane_doe"
|
||||||
|
formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }"
|
||||||
|
>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span class="input-group-text">@{{ instanceHost }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="formErrors.username" class="form-error">
|
||||||
|
{{ formErrors.username }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email" i18n>Email</label>
|
||||||
|
<input
|
||||||
|
type="text" id="email" i18n-placeholder placeholder="Email"
|
||||||
|
formControlName="email" [ngClass]="{ 'input-error': formErrors['email'] }"
|
||||||
|
>
|
||||||
|
<div *ngIf="formErrors.email" class="form-error">
|
||||||
|
{{ formErrors.email }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" i18n>Password</label>
|
||||||
|
<input
|
||||||
|
type="password" id="password" i18n-placeholder placeholder="Password"
|
||||||
|
formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
|
||||||
|
>
|
||||||
|
<div *ngIf="formErrors.password" class="form-error">
|
||||||
|
{{ formErrors.password }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group form-group-terms">
|
||||||
|
<my-peertube-checkbox
|
||||||
|
inputName="terms" formControlName="terms"
|
||||||
|
i18n-labelHtml
|
||||||
|
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"
|
||||||
|
></my-peertube-checkbox>
|
||||||
|
|
||||||
|
<div *ngIf="formErrors.terms" class="form-error">
|
||||||
|
{{ formErrors.terms }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { Component, EventEmitter, OnInit, Output } from '@angular/core'
|
||||||
|
import { AuthService } from '@app/core'
|
||||||
|
import { FormReactive, UserValidatorsService } from '../shared'
|
||||||
|
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
||||||
|
import { FormGroup } from '@angular/forms'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-signup-step-user',
|
||||||
|
templateUrl: './signup-step-user.component.html',
|
||||||
|
styleUrls: [ './signup.component.scss' ]
|
||||||
|
})
|
||||||
|
export class SignupStepUserComponent extends FormReactive implements OnInit {
|
||||||
|
@Output() formBuilt = new EventEmitter<FormGroup>()
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
protected formValidatorService: FormValidatorService,
|
||||||
|
private authService: AuthService,
|
||||||
|
private userValidatorsService: UserValidatorsService
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
get instanceHost () {
|
||||||
|
return window.location.host
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
this.buildForm({
|
||||||
|
username: this.userValidatorsService.USER_USERNAME,
|
||||||
|
password: this.userValidatorsService.USER_PASSWORD,
|
||||||
|
email: this.userValidatorsService.USER_EMAIL,
|
||||||
|
terms: this.userValidatorsService.USER_TERMS
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => this.formBuilt.emit(this.form))
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,64 +4,35 @@
|
||||||
Create an account
|
Create an account
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<my-success *ngIf="signupDone"></my-success>
|
||||||
<div *ngIf="info" class="alert alert-info">{{ info }}</div>
|
<div *ngIf="info" class="alert alert-info">{{ info }}</div>
|
||||||
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
|
<div *ngIf="success" class="alert alert-success">{{ success }}</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-left flex-wrap">
|
<div class="wrapper" *ngIf="!signupDone">
|
||||||
<form role="form" (ngSubmit)="signup()" [formGroup]="form">
|
<div>
|
||||||
<div class="form-group">
|
<my-custom-stepper linear *ngIf="!signupDone">
|
||||||
<label for="username" i18n>Username</label>
|
<cdk-step [stepControl]="formStepUser" i18n-label label="User information">
|
||||||
|
<my-signup-step-user (formBuilt)="onUserFormBuilt($event)"></my-signup-step-user>
|
||||||
|
|
||||||
<div class="input-group">
|
<button i18n cdkStepperNext [disabled]="!formStepUser || !formStepUser.valid">Next</button>
|
||||||
<input
|
</cdk-step>
|
||||||
type="text" id="username" i18n-placeholder placeholder="Example: jane_doe"
|
|
||||||
formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }"
|
<cdk-step [stepControl]="formStepChannel" i18n-label label="Channel information">
|
||||||
|
<my-signup-step-channel (formBuilt)="onChannelFormBuilt($event)" [username]="getUsername()"></my-signup-step-channel>
|
||||||
|
|
||||||
|
<button i18n cdkStepperNext (click)="signup()"
|
||||||
|
[disabled]="!formStepChannel || !formStepChannel.valid || hasSameChannelAndAccountNames()"
|
||||||
>
|
>
|
||||||
<div class="input-group-append">
|
Create my account
|
||||||
<span class="input-group-text">@{{ instanceHost }}</span>
|
</button>
|
||||||
</div>
|
</cdk-step>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="formErrors.username" class="form-error">
|
<cdk-step i18n-label label="Done" editable="false">
|
||||||
{{ formErrors.username }}
|
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
|
||||||
</div>
|
</cdk-step>
|
||||||
</div>
|
</my-custom-stepper>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label for="email" i18n>Email</label>
|
|
||||||
<input
|
|
||||||
type="text" id="email" i18n-placeholder placeholder="Email"
|
|
||||||
formControlName="email" [ngClass]="{ 'input-error': formErrors['email'] }"
|
|
||||||
>
|
|
||||||
<div *ngIf="formErrors.email" class="form-error">
|
|
||||||
{{ formErrors.email }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="password" i18n>Password</label>
|
|
||||||
<input
|
|
||||||
type="password" id="password" i18n-placeholder placeholder="Password"
|
|
||||||
formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
|
|
||||||
>
|
|
||||||
<div *ngIf="formErrors.password" class="form-error">
|
|
||||||
{{ formErrors.password }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group form-group-terms">
|
|
||||||
<my-peertube-checkbox
|
|
||||||
inputName="terms" formControlName="terms"
|
|
||||||
i18n-labelHtml 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"
|
|
||||||
></my-peertube-checkbox>
|
|
||||||
|
|
||||||
<div *ngIf="formErrors.terms" class="form-error">
|
|
||||||
{{ formErrors.terms }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="submit" i18n-value value="Signup" [disabled]="!form.valid || signupDone">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label i18n>Features found on this instance</label>
|
<label i18n>Features found on this instance</label>
|
||||||
|
|
|
@ -1,16 +1,32 @@
|
||||||
@import '_variables';
|
@import '_variables';
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
font-size: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
width: 450px;
|
||||||
|
|
||||||
|
@media screen and (max-width: 500px) {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
my-instance-features-table {
|
my-instance-features-table {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
|
||||||
margin: 0 60px 40px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group-terms {
|
.form-group-terms {
|
||||||
margin: 30px 0;
|
margin: 30px 0;
|
||||||
}
|
}
|
||||||
|
@ -25,15 +41,18 @@ form {
|
||||||
|
|
||||||
input:not([type=submit]) {
|
input:not([type=submit]) {
|
||||||
@include peertube-input-text(400px);
|
@include peertube-input-text(400px);
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
&#username {
|
&#username,
|
||||||
width: auto;
|
&#name {
|
||||||
|
width: auto !important;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=submit] {
|
input[type=submit],
|
||||||
|
button {
|
||||||
@include peertube-button;
|
@include peertube-button;
|
||||||
@include orange-button;
|
@include orange-button;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { AuthService, Notifier, RedirectService, ServerService } from '@app/core'
|
import { AuthService, Notifier, RedirectService, ServerService } from '@app/core'
|
||||||
import { UserCreate } from '../../../../shared'
|
import { UserService, UserValidatorsService } from '../shared'
|
||||||
import { FormReactive, UserService, UserValidatorsService } from '../shared'
|
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
import { UserRegister } from '@shared/models/users/user-register.model'
|
||||||
|
import { FormGroup } from '@angular/forms'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-signup',
|
selector: 'my-signup',
|
||||||
templateUrl: './signup.component.html',
|
templateUrl: './signup.component.html',
|
||||||
styleUrls: [ './signup.component.scss' ]
|
styleUrls: [ './signup.component.scss' ]
|
||||||
})
|
})
|
||||||
export class SignupComponent extends FormReactive implements OnInit {
|
export class SignupComponent {
|
||||||
info: string = null
|
info: string = null
|
||||||
error: string = null
|
error: string = null
|
||||||
|
success: string = null
|
||||||
signupDone = false
|
signupDone = false
|
||||||
|
|
||||||
|
formStepUser: FormGroup
|
||||||
|
formStepChannel: FormGroup
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
protected formValidatorService: FormValidatorService,
|
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private userValidatorsService: UserValidatorsService,
|
private userValidatorsService: UserValidatorsService,
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
|
@ -25,47 +28,55 @@ export class SignupComponent extends FormReactive implements OnInit {
|
||||||
private redirectService: RedirectService,
|
private redirectService: RedirectService,
|
||||||
private i18n: I18n
|
private i18n: I18n
|
||||||
) {
|
) {
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
get instanceHost () {
|
|
||||||
return window.location.host
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get requiresEmailVerification () {
|
get requiresEmailVerification () {
|
||||||
return this.serverService.getConfig().signup.requiresEmailVerification
|
return this.serverService.getConfig().signup.requiresEmailVerification
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
hasSameChannelAndAccountNames () {
|
||||||
this.buildForm({
|
return this.getUsername() === this.getChannelName()
|
||||||
username: this.userValidatorsService.USER_USERNAME,
|
}
|
||||||
password: this.userValidatorsService.USER_PASSWORD,
|
|
||||||
email: this.userValidatorsService.USER_EMAIL,
|
getUsername () {
|
||||||
terms: this.userValidatorsService.USER_TERMS
|
if (!this.formStepUser) return undefined
|
||||||
})
|
|
||||||
|
return this.formStepUser.value['username']
|
||||||
|
}
|
||||||
|
|
||||||
|
getChannelName () {
|
||||||
|
if (!this.formStepChannel) return undefined
|
||||||
|
|
||||||
|
return this.formStepChannel.value['name']
|
||||||
|
}
|
||||||
|
|
||||||
|
onUserFormBuilt (form: FormGroup) {
|
||||||
|
this.formStepUser = form
|
||||||
|
}
|
||||||
|
|
||||||
|
onChannelFormBuilt (form: FormGroup) {
|
||||||
|
this.formStepChannel = form
|
||||||
}
|
}
|
||||||
|
|
||||||
signup () {
|
signup () {
|
||||||
this.error = null
|
this.error = null
|
||||||
|
|
||||||
const userCreate: UserCreate = this.form.value
|
const body: UserRegister = Object.assign(this.formStepUser.value, this.formStepChannel.value)
|
||||||
|
|
||||||
this.userService.signup(userCreate).subscribe(
|
this.userService.signup(body).subscribe(
|
||||||
() => {
|
() => {
|
||||||
this.signupDone = true
|
this.signupDone = true
|
||||||
|
|
||||||
if (this.requiresEmailVerification) {
|
if (this.requiresEmailVerification) {
|
||||||
this.info = this.i18n('Welcome! Now please check your emails to verify your account and complete signup.')
|
this.info = this.i18n('Now please check your emails to verify your account and complete signup.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto login
|
// Auto login
|
||||||
this.authService.login(userCreate.username, userCreate.password)
|
this.authService.login(body.username, body.password)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
() => {
|
() => {
|
||||||
this.notifier.success(this.i18n('You are now logged in as {{username}}!', { username: userCreate.username }))
|
this.success = this.i18n('You are now logged in as {{username}}!', { username: body.username })
|
||||||
|
|
||||||
this.redirectService.redirectToHomepage()
|
|
||||||
},
|
},
|
||||||
|
|
||||||
err => this.error = err.message
|
err => this.error = err.message
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
|
|
||||||
import { SignupRoutingModule } from './signup-routing.module'
|
import { SignupRoutingModule } from './signup-routing.module'
|
||||||
import { SignupComponent } from './signup.component'
|
import { SignupComponent } from './signup.component'
|
||||||
import { SharedModule } from '../shared'
|
import { SharedModule } from '../shared'
|
||||||
|
import { CdkStepperModule } from '@angular/cdk/stepper'
|
||||||
|
import { SignupStepChannelComponent } from '@app/signup/signup-step-channel.component'
|
||||||
|
import { SignupStepUserComponent } from '@app/signup/signup-step-user.component'
|
||||||
|
import { CustomStepperComponent } from '@app/signup/custom-stepper.component'
|
||||||
|
import { SuccessComponent } from '@app/signup/success.component'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
SignupRoutingModule,
|
SignupRoutingModule,
|
||||||
SharedModule
|
SharedModule,
|
||||||
|
CdkStepperModule
|
||||||
],
|
],
|
||||||
|
|
||||||
declarations: [
|
declarations: [
|
||||||
SignupComponent
|
SignupComponent,
|
||||||
|
CustomStepperComponent,
|
||||||
|
SuccessComponent,
|
||||||
|
SignupStepChannelComponent,
|
||||||
|
SignupStepUserComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
exports: [
|
exports: [
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<!-- Thanks: Amit Singh Sansoya from https://codepen.io/amit3200/pen/zWMJOO -->
|
||||||
|
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 130.2 130.2">
|
||||||
|
<circle class="path circle" fill="none" stroke="#73AF55" stroke-width="6" stroke-miterlimit="10" cx="65.1" cy="65.1" r="62.1"/>
|
||||||
|
<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 "/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<p class="success">Welcome on PeerTube!</p>
|
After Width: | Height: | Size: 510 B |
|
@ -0,0 +1,74 @@
|
||||||
|
svg {
|
||||||
|
width: 100px;
|
||||||
|
display: block;
|
||||||
|
margin: 40px auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.path {
|
||||||
|
stroke-dasharray: 1000;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
|
||||||
|
&.circle {
|
||||||
|
-webkit-animation: dash .9s ease-in-out;
|
||||||
|
animation: dash .9s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.line {
|
||||||
|
stroke-dashoffset: 1000;
|
||||||
|
-webkit-animation: dash .9s .35s ease-in-out forwards;
|
||||||
|
animation: dash .9s .35s ease-in-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.check {
|
||||||
|
stroke-dashoffset: -100;
|
||||||
|
-webkit-animation: dash-check .9s .35s ease-in-out forwards;
|
||||||
|
animation: dash-check .9s .35s ease-in-out forwards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
text-align: center;
|
||||||
|
margin: 20px 0 60px;
|
||||||
|
font-size: 1.25em;
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
color: #73AF55;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@-webkit-keyframes dash {
|
||||||
|
0% {
|
||||||
|
stroke-dashoffset: 1000;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dash {
|
||||||
|
0% {
|
||||||
|
stroke-dashoffset: 1000;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes dash-check {
|
||||||
|
0% {
|
||||||
|
stroke-dashoffset: -100;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
stroke-dashoffset: 900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dash-check {
|
||||||
|
0% {
|
||||||
|
stroke-dashoffset: -100;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
stroke-dashoffset: 900;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Component } from '@angular/core'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-success',
|
||||||
|
templateUrl: './success.component.html',
|
||||||
|
styleUrls: [ './success.component.scss' ]
|
||||||
|
})
|
||||||
|
export class SuccessComponent {
|
||||||
|
|
||||||
|
}
|
|
@ -331,7 +331,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin peertube-checkbox ($border-width) {
|
@mixin peertube-checkbox ($border-width) {
|
||||||
display: none;
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
|
||||||
|
&:focus + span {
|
||||||
|
outline: auto;
|
||||||
|
}
|
||||||
|
|
||||||
& + span {
|
& + span {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -70,6 +70,12 @@ const usersRegisterValidator = [
|
||||||
.end()
|
.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (body.channel.name === body.username) {
|
||||||
|
return res.status(400)
|
||||||
|
.send({ error: 'Channel name cannot be the same than user username.' })
|
||||||
|
.end()
|
||||||
|
}
|
||||||
|
|
||||||
const existing = await ActorModel.loadLocalByName(body.channel.name)
|
const existing = await ActorModel.loadLocalByName(body.channel.name)
|
||||||
if (existing) {
|
if (existing) {
|
||||||
return res.status(409)
|
return res.status(409)
|
||||||
|
|
|
@ -737,6 +737,13 @@ describe('Test users API validators', function () {
|
||||||
await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
|
await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should fail with a channel name that is the same than user username', async function () {
|
||||||
|
const source = { username: 'super_user', channel: { name: 'super_user', displayName: 'display name' } }
|
||||||
|
const fields = immutableAssign(baseCorrectParams, source)
|
||||||
|
|
||||||
|
await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
|
||||||
|
})
|
||||||
|
|
||||||
it('Should fail with an existing channel', async function () {
|
it('Should fail with an existing channel', async function () {
|
||||||
const videoChannelAttributesArg = { name: 'existing_channel', displayName: 'hello', description: 'super description' }
|
const videoChannelAttributesArg = { name: 'existing_channel', displayName: 'hello', description: 'super description' }
|
||||||
await addVideoChannel(server.url, server.accessToken, videoChannelAttributesArg)
|
await addVideoChannel(server.url, server.accessToken, videoChannelAttributesArg)
|
||||||
|
|
Loading…
Reference in New Issue