Redesign register steps
|
@ -4,7 +4,7 @@
|
|||
<div class="col-12 col-lg-4 col-xl-3"></div>
|
||||
<div class="col-12 col-lg-8">
|
||||
|
||||
<div class="callout callout-info">
|
||||
<div class="callout callout-orange">
|
||||
<span i18n>
|
||||
Estimating a server's capacity to transcode and stream videos isn't easy and we can't tune PeerTube automatically.
|
||||
</span>
|
||||
|
|
|
@ -1,24 +1,29 @@
|
|||
<section class="container">
|
||||
<section>
|
||||
<header *ngIf="steps.length > 2">
|
||||
<ng-container *ngFor="let step of steps; let i = index; let isLast = last;">
|
||||
<div
|
||||
class="step-info" [ngClass]="{ active: selectedIndex === i, completed: isCompleted(step), 'c-hand': isAccessible(i) }" [attr.aria-current]="selectedIndex === i"
|
||||
(click)="onClick(i)"
|
||||
>
|
||||
<div class="step-index">
|
||||
<ng-container *ngIf="!isCompleted(step)"><span class="visually-hidden" i18n>Step</span> {{ i + 1 }}</ng-container>
|
||||
<my-global-icon *ngIf="isCompleted(step)" iconName="tick"></my-global-icon>
|
||||
<div class="header-steps">
|
||||
<ng-container *ngFor="let step of steps; let i = index; let isLast = last;">
|
||||
<div
|
||||
class="step-info" [ngClass]="{ active: selectedIndex === i, completed: isCompleted(step), 'c-hand': isAccessible(step) }" [attr.aria-current]="selectedIndex === i"
|
||||
(click)="onClick(i)"
|
||||
>
|
||||
<div class="step-index">
|
||||
<span class="visually-hidden" i18n>Step</span> {{ i + 1 }}
|
||||
|
||||
<div class="completed-icon" *ngIf="isCompleted(step)">
|
||||
<my-global-icon iconName="tick"></my-global-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step-label">{{ step.label }}</div>
|
||||
</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>
|
||||
<!-- Do no display if this is the last child -->
|
||||
<div *ngIf="!isLast" class="connector"></div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div [style.display]="selected ? 'block' : 'none'">
|
||||
<div class="margin-content" [style.display]="selected ? 'block' : 'none'">
|
||||
<ng-container [ngTemplateOutlet]="selected.content"></ng-container>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -2,76 +2,113 @@
|
|||
@use '_variables' as *;
|
||||
@use '_mixins' as *;
|
||||
|
||||
$grey-color: #9CA3AB;
|
||||
$index-block-height: 32px;
|
||||
|
||||
.container {
|
||||
@include padding-left(0);
|
||||
@include padding-right(0);
|
||||
max-width: unset !important;
|
||||
}
|
||||
$index-block-height: 40px;
|
||||
|
||||
header {
|
||||
margin-bottom: 40px;
|
||||
padding-bottom: 60px;
|
||||
width: 100%;
|
||||
background-color: pvar(--mainColorVeryLight);
|
||||
}
|
||||
|
||||
.header-steps {
|
||||
max-width: 800px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 15px;
|
||||
margin-bottom: 30px;
|
||||
margin: auto;
|
||||
|
||||
.step-info {
|
||||
color: $grey-color;
|
||||
// Useful on small screens
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.step-index {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: $index-block-height;
|
||||
height: $index-block-height;
|
||||
border-radius: $index-block-height;
|
||||
border: 1px solid pvar(--mainColor);
|
||||
margin-bottom: 10px;
|
||||
font-size: 24px;
|
||||
position: relative;
|
||||
|
||||
.completed-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 16px;
|
||||
background-color: pvar(--mainBackgroundColor);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: $index-block-height;
|
||||
border: 1px solid pvar(--mainColor);
|
||||
|
||||
&:not(.c-hand) {
|
||||
cursor: default;
|
||||
}
|
||||
my-global-icon {
|
||||
@include apply-svg-color(pvar(--mainColor));
|
||||
|
||||
.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;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my-global-icon {
|
||||
@include apply-svg-color(pvar(--mainBackgroundColor));
|
||||
.step-label {
|
||||
width: max-content;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
.step-info {
|
||||
color: pvar(--mainColor);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: $index-block-height;
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
|
||||
&.c-hand {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.active,
|
||||
&.completed {
|
||||
.step-index {
|
||||
background-color: pvar(--mainColor);
|
||||
color: pvar(--mainBackgroundColor);
|
||||
}
|
||||
|
||||
.step-label {
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
&.active,
|
||||
&.completed {
|
||||
.step-index {
|
||||
border-color: pvar(--mainColor);
|
||||
background-color: pvar(--mainColor);
|
||||
color: pvar(--mainBackgroundColor);
|
||||
}
|
||||
|
||||
.step-label {
|
||||
color: pvar(--mainColor);
|
||||
}
|
||||
}
|
||||
|
||||
&.completed {
|
||||
cursor: pointer;
|
||||
color: pvar(--mainColor);
|
||||
}
|
||||
}
|
||||
|
||||
.connector {
|
||||
flex: auto;
|
||||
margin: math.div($index-block-height, 2) 10px 0 10px;
|
||||
height: 2px;
|
||||
background-color: $grey-color;
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.connector {
|
||||
flex: auto;
|
||||
margin: math.div($index-block-height, 2) 10px 0 10px;
|
||||
height: 2px;
|
||||
background-color: pvar(--mainColor);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
@media screen and (min-width: $small-view) {
|
||||
.margin-content {
|
||||
max-width: 1000px;
|
||||
margin:auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $small-view) {
|
||||
.step-label {
|
||||
width: auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,13 +14,10 @@ export class CustomStepperComponent extends CdkStepper {
|
|||
}
|
||||
|
||||
isCompleted (step: CdkStep) {
|
||||
return step.stepControl?.dirty && step.stepControl.valid
|
||||
return step.completed
|
||||
}
|
||||
|
||||
isAccessible (index: number) {
|
||||
const stepsCompletedMap = this.steps.map(step => this.isCompleted(step))
|
||||
return index === 0
|
||||
? true
|
||||
: stepsCompletedMap[index - 1]
|
||||
isAccessible (step: CdkStep) {
|
||||
return step.editable && step.completed
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
<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 i18n>
|
||||
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="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>
|
||||
|
||||
<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-text">@{{ instanceHost }}</div>
|
||||
</div>
|
||||
|
||||
<div class="name-information" i18n>
|
||||
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.
|
||||
</div>
|
||||
|
||||
<div *ngIf="formErrors.name" class="form-error">
|
||||
{{ formErrors.name }}
|
||||
</div>
|
||||
|
||||
<div *ngIf="isSameThanUsername()" class="form-error" i18n>
|
||||
Channel name cannot be the same as your account name. You can click on the first step to update your account name.
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
|
@ -1,64 +0,0 @@
|
|||
<form role="form" [formGroup]="form">
|
||||
|
||||
<div class="capability-information alert alert-info" i18n *ngIf="videoUploadDisabled">
|
||||
Video uploads are disabled on this instance, hence your account won't be able to upload videos.
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="displayName" i18n>Display name</label>
|
||||
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="text" id="displayName" placeholder="John Doe"
|
||||
formControlName="displayName" [ngClass]="{ 'input-error': formErrors['displayName'] }"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div *ngIf="formErrors.displayName" class="form-error">
|
||||
{{ formErrors.displayName }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="username" i18n>Username</label>
|
||||
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="text" id="username" i18n-placeholder="Username choice placeholder in the registration form" placeholder="e.g. jane_doe"
|
||||
formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }"
|
||||
>
|
||||
<span class="input-group-text">@{{ instanceHost }}</span>
|
||||
</div>
|
||||
|
||||
<div class="name-information" i18n>
|
||||
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.
|
||||
</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" class="form-control" [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>
|
||||
<my-input-text formControlName="password" inputId="password"
|
||||
i18n-placeholder placeholder="Password"
|
||||
[ngClass]="{ 'input-error': formErrors['password'] }"
|
||||
autocomplete="new-password"></my-input-text>
|
||||
<div *ngIf="formErrors.password" class="form-error">
|
||||
{{ formErrors.password }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
|
@ -1,64 +1,121 @@
|
|||
<div class="margin-content">
|
||||
<div>
|
||||
|
||||
<div class="signup-disabled" *ngIf="signupDisabled">
|
||||
<div class="alert alert-warning" i18n>Signup is not enabled on this instance.</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="!signupDisabled">
|
||||
<div i18n class="title-page title-page-single">
|
||||
<h1 i18n class="header-title">
|
||||
<strong class="underline-orange">{{ instanceName }}</strong>
|
||||
>
|
||||
Create an account
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
<my-signup-success *ngIf="signupDone" [message]="success"></my-signup-success>
|
||||
<div *ngIf="info" class="alert alert-info">{{ info }}</div>
|
||||
<div class="register-content">
|
||||
<my-custom-stepper linear>
|
||||
|
||||
<div class="wrapper" [hidden]="signupDone">
|
||||
<div class="register-form">
|
||||
<my-custom-stepper linear *ngIf="!signupDone">
|
||||
<cdk-step [stepControl]="formStepTerms" i18n-label="Stepper label for the registration page describing terms of service" label="Terms">
|
||||
<div class="instance-information">
|
||||
<my-instance-about-accordion
|
||||
(init)="onInstanceAboutAccordionInit($event)" [panels]="instanceInformationPanels"
|
||||
pluginScope="signup" pluginHook="filter:signup.instance-about-plugin-panels.create.result"
|
||||
></my-instance-about-accordion>
|
||||
</div>
|
||||
<cdk-step i18n-label label="About" [editable]="!signupSuccess">
|
||||
<my-signup-step-title mascotImageName="about" i18n>
|
||||
<strong>Create an account</strong>
|
||||
<div>on {{ instanceName }}</div>
|
||||
</my-signup-step-title>
|
||||
|
||||
<my-register-step-terms
|
||||
[hasCodeOfConduct]="!!aboutHtml.codeOfConduct"
|
||||
[minimumAge]="minimumAge"
|
||||
(formBuilt)="onTermsFormBuilt($event)" (termsClick)="onTermsClick()" (codeOfConductClick)="onCodeOfConductClick()"
|
||||
></my-register-step-terms>
|
||||
<my-register-step-about [videoUploadDisabled]="videoUploadDisabled"></my-register-step-about>
|
||||
|
||||
<div class="step-buttons">
|
||||
<a i18n class="skip-step underline-orange" routerLink="/login">
|
||||
<strong>I already have an account</strong>, I log in
|
||||
</a>
|
||||
|
||||
<button i18n cdkStepperNext>I create an account</button>
|
||||
</div>
|
||||
</cdk-step>
|
||||
|
||||
<cdk-step [stepControl]="formStepTerms" i18n-label label="Terms" [editable]="!signupSuccess">
|
||||
<my-signup-step-title mascotImageName="terms" i18n>
|
||||
<strong>Terms</strong>
|
||||
<div>of {{ instanceName }}</div>
|
||||
</my-signup-step-title>
|
||||
|
||||
<my-instance-about-accordion
|
||||
[displayInstanceName]="false"
|
||||
(init)="onInstanceAboutAccordionInit($event)" [panels]="instanceInformationPanels"
|
||||
pluginScope="signup" pluginHook="filter:signup.instance-about-plugin-panels.create.result"
|
||||
></my-instance-about-accordion>
|
||||
|
||||
<my-register-step-terms
|
||||
[hasCodeOfConduct]="!!aboutHtml.codeOfConduct"
|
||||
[minimumAge]="minimumAge"
|
||||
(formBuilt)="onTermsFormBuilt($event)" (termsClick)="onTermsClick()" (codeOfConductClick)="onCodeOfConductClick()"
|
||||
></my-register-step-terms>
|
||||
|
||||
<div class="step-buttons">
|
||||
<button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button>
|
||||
<button cdkStepperNext [disabled]="!formStepTerms || !formStepTerms.valid">{{ defaultNextStepButtonLabel }}</button>
|
||||
</cdk-step>
|
||||
</div>
|
||||
</cdk-step>
|
||||
|
||||
<cdk-step [stepControl]="formStepUser" i18n-label="Stepper label for the registration page asking user information" label="User">
|
||||
<my-register-step-user (formBuilt)="onUserFormBuilt($event)" [videoUploadDisabled]="videoUploadDisabled"></my-register-step-user>
|
||||
<cdk-step [stepControl]="formStepUser" label="My account" [editable]="!signupSuccess">
|
||||
<my-signup-step-title mascotImageName="account" i18n>
|
||||
<strong>Setup</strong>
|
||||
<div>your account</div>
|
||||
</my-signup-step-title>
|
||||
|
||||
<my-register-step-user
|
||||
(formBuilt)="onUserFormBuilt($event)"
|
||||
[videoUploadDisabled]="videoUploadDisabled" [requiresEmailVerification]="requiresEmailVerification"
|
||||
></my-register-step-user>
|
||||
|
||||
<div class="step-buttons">
|
||||
<button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button>
|
||||
<button cdkStepperNext [disabled]="!formStepUser || !formStepUser.valid" (click)="videoUploadDisabled && signup()">{{ stepUserButtonLabel }}</button>
|
||||
</cdk-step>
|
||||
</div>
|
||||
</cdk-step>
|
||||
|
||||
<cdk-step [stepControl]="formStepChannel" i18n-label="Stepper label for the registration page asking information about the default channel" label="Channel" *ngIf="!videoUploadDisabled">
|
||||
<my-register-step-channel (formBuilt)="onChannelFormBuilt($event)" [username]="getUsername()"></my-register-step-channel>
|
||||
<cdk-step *ngIf="!videoUploadDisabled" [optional]="true" [stepControl]="formStepChannel" i18n-label label="My channel" [editable]="!signupSuccess">
|
||||
<my-signup-step-title mascotImageName="channel" i18n>
|
||||
<div>Create</div>
|
||||
<strong>your first channel</strong>
|
||||
</my-signup-step-title>
|
||||
|
||||
<my-register-step-channel
|
||||
(formBuilt)="onChannelFormBuilt($event)"
|
||||
[videoQuota]="videoQuota" [instanceName]="instanceName" [username]="getUsername()"
|
||||
></my-register-step-channel>
|
||||
|
||||
<div class="step-buttons">
|
||||
<button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button>
|
||||
|
||||
<div class="skip-step">
|
||||
<span class="underline-orange" role="button" (click)="skipChannelCreation()">
|
||||
<strong i18n>I don't want to create a channel</strong>
|
||||
</span>
|
||||
|
||||
<div class="skip-step-description" i18n>You will be able to create a channel later</div>
|
||||
</div>
|
||||
|
||||
<button cdkStepperNext [disabled]="!formStepChannel || !formStepChannel.valid || hasSameChannelAndAccountNames()" (click)="signup()" i18n>
|
||||
Create my account
|
||||
</button>
|
||||
</cdk-step>
|
||||
</div>
|
||||
</cdk-step>
|
||||
|
||||
<cdk-step i18n-label label="Done" editable="false">
|
||||
<div *ngIf="!signupDone && !error" class="done-loader">
|
||||
<my-loader [loading]="true"></my-loader>
|
||||
<cdk-step #lastStep i18n-label label="Done!" [editable]="false">
|
||||
<div *ngIf="!signupSuccess && !signupError" class="done-loader">
|
||||
<my-loader [loading]="true"></my-loader>
|
||||
|
||||
<div i18n>PeerTube is creating your account...</div>
|
||||
</div>
|
||||
<div i18n>PeerTube is creating your account...</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
|
||||
</cdk-step>
|
||||
</my-custom-stepper>
|
||||
</div>
|
||||
<div *ngIf="signupError" class="alert alert-danger">{{ signupError }}</div>
|
||||
|
||||
<my-signup-success *ngIf="signupSuccess" [requiresEmailVerification]="requiresEmailVerification"></my-signup-success>
|
||||
|
||||
<div *ngIf="signupError" class="steps-button">
|
||||
<button cdkStepperPrevious>{{ defaultPreviousStepButtonLabel }}</button>
|
||||
</div>
|
||||
</cdk-step>
|
||||
</my-custom-stepper>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@use '_mixins' as *;
|
||||
|
||||
.alert {
|
||||
font-size: 15px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -10,61 +10,75 @@
|
|||
padding-top: 30vh;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.register-form {
|
||||
max-width: 600px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.register-form,
|
||||
.instance-information {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.instance-information {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.header-title {
|
||||
font-weight: normal;
|
||||
font-size: 15px;
|
||||
background-color: pvar(--mainColorVeryLight);
|
||||
padding: 35px 25px 15px 25px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input:not([type=submit]) {
|
||||
@include peertube-input-text(100%);
|
||||
.register-content {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
my-instance-about-accordion {
|
||||
display: block;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
&#username,
|
||||
&#name {
|
||||
width: auto !important;
|
||||
flex-grow: 1;
|
||||
.step-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
.skip-step {
|
||||
@include margin-right(30px);
|
||||
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.skip-step-description {
|
||||
margin-top: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.underline-orange {
|
||||
color: pvar(--mainForegroundColor);
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
button,
|
||||
.skip-step {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.skip-step,
|
||||
button[cdkStepperNext] {
|
||||
@include margin-left(auto);
|
||||
}
|
||||
|
||||
.skip-step + button[cdkStepperNext] {
|
||||
@include margin-left(0);
|
||||
}
|
||||
}
|
||||
|
||||
input[type=submit],
|
||||
button {
|
||||
@include peertube-button;
|
||||
@include peertube-button-big;
|
||||
|
||||
&[cdkStepperNext] {
|
||||
@include orange-button;
|
||||
|
||||
// Chrome does not support inline-end
|
||||
float: right;
|
||||
float: inline-end;
|
||||
}
|
||||
|
||||
&[cdkStepperPrevious] {
|
||||
@include grey-button;
|
||||
|
||||
// Chrome does not support inline-start
|
||||
float: left;
|
||||
float: inline-start;
|
||||
}
|
||||
}
|
||||
|
||||
.name-information {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.done-loader {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@ -73,13 +87,16 @@ button {
|
|||
|
||||
my-loader {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .loader div {
|
||||
border-color: pvar(--mainColor) transparent transparent transparent;
|
||||
}
|
||||
@media screen and (max-width: $small-view) {
|
||||
.step-buttons {
|
||||
justify-content: space-between;
|
||||
|
||||
+ div {
|
||||
font-size: 15px;
|
||||
.skip-step,
|
||||
button[cdkStepperNext] {
|
||||
@include margin-left(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { CdkStep } from '@angular/cdk/stepper'
|
||||
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||
import { FormGroup } from '@angular/forms'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { AuthService } from '@app/core'
|
||||
|
@ -15,13 +16,15 @@ import { ServerConfig } from '@shared/models/server'
|
|||
styleUrls: [ './register.component.scss' ]
|
||||
})
|
||||
export class RegisterComponent implements OnInit {
|
||||
@ViewChild('lastStep') lastStep: CdkStep
|
||||
|
||||
accordion: NgbAccordion
|
||||
info: string = null
|
||||
error: string = null
|
||||
success: string = null
|
||||
signupDone = false
|
||||
|
||||
signupError: string
|
||||
signupSuccess = false
|
||||
|
||||
videoUploadDisabled: boolean
|
||||
videoQuota: number
|
||||
|
||||
formStepTerms: FormGroup
|
||||
formStepUser: FormGroup
|
||||
|
@ -39,8 +42,8 @@ export class RegisterComponent implements OnInit {
|
|||
moderation: false
|
||||
}
|
||||
|
||||
defaultPreviousStepButtonLabel = $localize`:Button on the registration form to go to the previous step:Back`
|
||||
defaultNextStepButtonLabel = $localize`:Button on the registration form to go to the previous step:Next`
|
||||
defaultPreviousStepButtonLabel = $localize`:Button on the registration form to go to the previous step:Go to the previous step`
|
||||
defaultNextStepButtonLabel = $localize`:Button on the registration form to go to the previous step:Go to the next step`
|
||||
stepUserButtonLabel = this.defaultNextStepButtonLabel
|
||||
|
||||
signupDisabled = false
|
||||
|
@ -62,7 +65,11 @@ export class RegisterComponent implements OnInit {
|
|||
return this.serverConfig.signup.minimumAge
|
||||
}
|
||||
|
||||
ngOnInit (): void {
|
||||
get instanceName () {
|
||||
return this.serverConfig.instance.name
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.serverConfig = this.route.snapshot.data.serverConfig
|
||||
|
||||
if (this.serverConfig.signup.allowed === false || this.serverConfig.signup.allowedForCurrentIP === false) {
|
||||
|
@ -70,7 +77,9 @@ export class RegisterComponent implements OnInit {
|
|||
return
|
||||
}
|
||||
|
||||
this.videoUploadDisabled = this.serverConfig.user.videoQuota === 0
|
||||
this.videoQuota = this.serverConfig.user.videoQuota
|
||||
this.videoUploadDisabled = this.videoQuota === 0
|
||||
|
||||
this.stepUserButtonLabel = this.videoUploadDisabled
|
||||
? $localize`:Button on the registration form to finalize the account and channel creation:Signup`
|
||||
: this.defaultNextStepButtonLabel
|
||||
|
@ -120,21 +129,31 @@ export class RegisterComponent implements OnInit {
|
|||
this.aboutHtml = instanceAboutAccordion.aboutHtml
|
||||
}
|
||||
|
||||
skipChannelCreation () {
|
||||
this.formStepChannel.reset()
|
||||
this.lastStep.select()
|
||||
this.signup()
|
||||
}
|
||||
|
||||
async signup () {
|
||||
this.error = null
|
||||
this.signupError = undefined
|
||||
|
||||
const body: UserRegister = await this.hooks.wrapObject(
|
||||
Object.assign(this.formStepUser.value, { channel: this.videoUploadDisabled ? undefined : this.formStepChannel.value }),
|
||||
{
|
||||
...this.formStepUser.value,
|
||||
|
||||
channel: this.formStepChannel?.value?.name
|
||||
? this.formStepChannel.value
|
||||
: undefined
|
||||
},
|
||||
'signup',
|
||||
'filter:api.signup.registration.create.params'
|
||||
)
|
||||
|
||||
this.userSignupService.signup(body).subscribe({
|
||||
next: () => {
|
||||
this.signupDone = true
|
||||
|
||||
if (this.requiresEmailVerification) {
|
||||
this.info = $localize`Now please check your emails to verify your account and complete signup.`
|
||||
this.signupSuccess = true
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -142,17 +161,17 @@ export class RegisterComponent implements OnInit {
|
|||
this.authService.login(body.username, body.password)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.success = $localize`You are now logged in as ${body.username}!`
|
||||
this.signupSuccess = true
|
||||
},
|
||||
|
||||
error: err => {
|
||||
this.error = err.message
|
||||
this.signupError = err.message
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
error: err => {
|
||||
this.error = err.message
|
||||
this.signupError = err.message
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,15 +2,15 @@ import { CdkStepperModule } from '@angular/cdk/stepper'
|
|||
import { NgModule } from '@angular/core'
|
||||
import { SharedSignupModule } from '@app/+signup/shared/shared-signup.module'
|
||||
import { SharedInstanceModule } from '@app/shared/shared-instance'
|
||||
import { SharedMainModule } from '@app/shared/shared-main'
|
||||
import { CustomStepperComponent } from './custom-stepper.component'
|
||||
import { RegisterRoutingModule } from './register-routing.module'
|
||||
import { RegisterStepChannelComponent } from './register-step-channel.component'
|
||||
import { RegisterStepTermsComponent } from './register-step-terms.component'
|
||||
import { RegisterStepUserComponent } from './register-step-user.component'
|
||||
import { RegisterComponent } from './register.component'
|
||||
import { RegisterStepAboutComponent, RegisterStepChannelComponent, RegisterStepTermsComponent, RegisterStepUserComponent } from './steps'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
SharedMainModule,
|
||||
RegisterRoutingModule,
|
||||
|
||||
CdkStepperModule,
|
||||
|
@ -25,7 +25,8 @@ import { RegisterComponent } from './register.component'
|
|||
CustomStepperComponent,
|
||||
RegisterStepChannelComponent,
|
||||
RegisterStepTermsComponent,
|
||||
RegisterStepUserComponent
|
||||
RegisterStepUserComponent,
|
||||
RegisterStepAboutComponent
|
||||
],
|
||||
|
||||
exports: [
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export * from './register-step-about.component'
|
||||
export * from './register-step-channel.component'
|
||||
export * from './register-step-terms.component'
|
||||
export * from './register-step-user.component'
|
|
@ -0,0 +1,39 @@
|
|||
<div class="why">
|
||||
<h3 i18n>Why creating an account?</h3>
|
||||
|
||||
<p i18n>
|
||||
As you probably noticed: creating an account is not necessary to watch video son {{ instanceName }}.
|
||||
<br />
|
||||
However, creating an account on {{ instanceName }} will allow you to:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li i18n><strong>Comment</strong> videos</li>
|
||||
<li i18n><strong>Subscribe</strong> to channels to be notified of new videos</li>
|
||||
<li i18n>Have access to your <strong>watch history</strong></li>
|
||||
<li *ngIf="!videoUploadDisabled" i18n>Create your channel to <strong>publish videos</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 i18n>You're using Mastodon, ActivityPub or a RSS feed aggregator?</h4>
|
||||
|
||||
<p i18n>
|
||||
You can already follow {{ instanceName }} using your favorite tool.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="callout callout-orange callout-light">
|
||||
<div class="mascot-container" style="min-width: 140px">
|
||||
<img class="mascot" width="140px" height="160px" src="/client/assets/images/mascot/happy.svg" alt="mascot"/>
|
||||
</div>
|
||||
|
||||
<div class="callout-content">
|
||||
<h4 i18>This website is a GAFAM alternative</h4>
|
||||
|
||||
<p i18n>
|
||||
{{ 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.
|
||||
<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
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,53 @@
|
|||
@use '_variables' as *;
|
||||
@use '_mixins' as *;
|
||||
|
||||
h3 {
|
||||
font-weight: $font-bold;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 18px;
|
||||
font-weight: $font-bold;
|
||||
}
|
||||
|
||||
.why {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.callout {
|
||||
margin: 75px auto 25px;
|
||||
border-width: 2px;
|
||||
display: flex;
|
||||
|
||||
.mascot-container {
|
||||
position: relative;
|
||||
|
||||
.mascot {
|
||||
position: absolute;
|
||||
top: -65px;
|
||||
}
|
||||
}
|
||||
|
||||
.callout-content {
|
||||
margin-left: 30px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $small-view) {
|
||||
.callout {
|
||||
margin-top: 20px;
|
||||
|
||||
.mascot-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.callout-content {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
import { ServerService } from '@app/core'
|
||||
|
||||
@Component({
|
||||
selector: 'my-register-step-about',
|
||||
templateUrl: './register-step-about.component.html',
|
||||
styleUrls: [ './register-step-about.component.scss' ]
|
||||
})
|
||||
export class RegisterStepAboutComponent {
|
||||
@Input() videoUploadDisabled: boolean
|
||||
|
||||
constructor (private serverService: ServerService) {
|
||||
|
||||
}
|
||||
|
||||
get instanceName () {
|
||||
return this.serverService.getHTMLConfig().instance.name
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<div class="mb-5">
|
||||
<p i18n>
|
||||
You want to <strong>publish videos</strong> on {{ instanceName }}? Then you need to create your first <strong>channel</strong>.
|
||||
</p>
|
||||
|
||||
<p i18n>
|
||||
You might want to <strong>create a channel by theme:</strong> for example, you can create a channel named "SweetMelodies"
|
||||
to publish your piano concerts and another one "Ecology" in which you publish your videos talking about ecology.
|
||||
</p>
|
||||
|
||||
<p i18n *ngIf="videoQuota !== -1">
|
||||
{{ instanceName }} administrators allow you to publish up to <strong>{{ videoQuota | bytes: 0 }} of videos</strong> on their website.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form role="form" [formGroup]="form">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-12 col-xl-6 form-group">
|
||||
<label for="displayName" i18n>Channel display name</label>
|
||||
|
||||
<div i18n class="form-group-description">This is the name that will be publicly visible by other users.</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="text" id="displayName" i18n-placeholder placeholder="Example: Sweet Melodies"
|
||||
formControlName="displayName" [ngClass]="{ 'input-error': formErrors['displayName'] }"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div *ngIf="formErrors.displayName" class="form-error">{{ formErrors.displayName }}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 col-xl-6 form-group">
|
||||
<label for="name" i18n>Channel identifier</label>
|
||||
|
||||
<div i18n class="form-group-description">This is the name that will be displayed in your profile URL.</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="text" id="name" i18n-placeholder placeholder="Example: sweetmelodies24"
|
||||
formControlName="name" [ngClass]="{ 'input-error': formErrors['name'] }"
|
||||
>
|
||||
<div class="input-group-text">@{{ instanceHost }}</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="formErrors.name" class="form-error">{{ formErrors.name }}</div>
|
||||
|
||||
<div *ngIf="isSameThanUsername()" class="form-error" i18n>
|
||||
Channel identifier cannot be the same as your account name. You can click on the first step to update your account name.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
|
@ -9,10 +9,13 @@ import { UserSignupService } from '@app/shared/shared-users'
|
|||
@Component({
|
||||
selector: 'my-register-step-channel',
|
||||
templateUrl: './register-step-channel.component.html',
|
||||
styleUrls: [ './register.component.scss' ]
|
||||
styleUrls: [ './step.component.scss' ]
|
||||
})
|
||||
export class RegisterStepChannelComponent extends FormReactive implements OnInit {
|
||||
@Input() username: string
|
||||
@Input() instanceName: string
|
||||
@Input() videoQuota: number
|
||||
|
||||
@Output() formBuilt = new EventEmitter<FormGroup>()
|
||||
|
||||
constructor (
|
|
@ -4,15 +4,13 @@
|
|||
<ng-template ptTemplate="label">
|
||||
<ng-container i18n>
|
||||
I am at least {{ minimumAge }} years old and agree
|
||||
to the <a class="terms-anchor" (click)="onTermsClick($event)" href='#'>Terms</a>
|
||||
to the <a class="link-orange" (click)="onTermsClick($event)" href='#'>Terms</a>
|
||||
<ng-container *ngIf="hasCodeOfConduct"> and to the <a (click)="onCodeOfConductClick($event)" href='#'>Code of Conduct</a></ng-container>
|
||||
of this instance
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</my-peertube-checkbox>
|
||||
|
||||
<div *ngIf="formErrors.terms" class="form-error">
|
||||
{{ formErrors.terms }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.terms" class="form-error">{{ formErrors.terms }}</div>
|
||||
</div>
|
||||
</form>
|
|
@ -8,7 +8,7 @@ import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
|||
@Component({
|
||||
selector: 'my-register-step-terms',
|
||||
templateUrl: './register-step-terms.component.html',
|
||||
styleUrls: [ './register.component.scss' ]
|
||||
styleUrls: [ './step.component.scss' ]
|
||||
})
|
||||
export class RegisterStepTermsComponent extends FormReactive implements OnInit {
|
||||
@Input() hasCodeOfConduct = false
|
|
@ -0,0 +1,73 @@
|
|||
<div class="alert pt-alert-primary" i18n *ngIf="videoUploadDisabled">
|
||||
Video uploads are disabled on this instance, hence your account won't be able to upload videos.
|
||||
</div>
|
||||
|
||||
<form role="form" [formGroup]="form">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-12 col-xl-6 form-group">
|
||||
<label for="displayName" i18n>Public name</label>
|
||||
|
||||
<div class="form-group-description" i18n>
|
||||
This is the name that will be publicly visible by other users.
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="text" id="displayName" i18n-placeholder placeholder="Example: John Doe"
|
||||
formControlName="displayName" [ngClass]="{ 'input-error': formErrors['displayName'] }"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div *ngIf="formErrors.displayName" class="form-error">{{ formErrors.displayName }}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 col-xl-6 form-group">
|
||||
<label for="username" i18n>Username</label>
|
||||
|
||||
<div class="form-group-description" i18n>
|
||||
This is the name that will be displayed in your profile URL.
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="text" id="username" i18n-placeholder placeholder="Example: john_doe58"
|
||||
formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }"
|
||||
>
|
||||
<span class="input-group-text">@{{ instanceHost }}</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="formErrors.username" class="form-error">{{ formErrors.username }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-xl-6 form-group">
|
||||
<label for="email" i18n>Email</label>
|
||||
|
||||
<div *ngIf="requiresEmailVerification" class="form-group-description" i18n>
|
||||
This email address will be used to validate your account.
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="text" id="email" i18n-placeholder placeholder="Example: john@example.com"
|
||||
formControlName="email" class="form-control" [ngClass]="{ 'input-error': formErrors['email'] }"
|
||||
>
|
||||
|
||||
<div *ngIf="formErrors.email" class="form-error">{{ formErrors.email }}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 col-xl-6 form-group">
|
||||
<label for="password" i18n>Password</label>
|
||||
|
||||
<div class="form-group-description">{{ getMinPasswordLengthMessage() }}</div>
|
||||
|
||||
<my-input-text
|
||||
formControlName="password" inputId="password"
|
||||
[ngClass]="{ 'input-error': formErrors['password'] }" autocomplete="new-password"
|
||||
></my-input-text>
|
||||
|
||||
<div *ngIf="formErrors.password" class="form-error">{{ formErrors.password }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
|
@ -14,10 +14,11 @@ import { UserSignupService } from '@app/shared/shared-users'
|
|||
@Component({
|
||||
selector: 'my-register-step-user',
|
||||
templateUrl: './register-step-user.component.html',
|
||||
styleUrls: [ './register.component.scss' ]
|
||||
styleUrls: [ './step.component.scss' ]
|
||||
})
|
||||
export class RegisterStepUserComponent extends FormReactive implements OnInit {
|
||||
@Input() videoUploadDisabled = false
|
||||
@Input() requiresEmailVerification = false
|
||||
|
||||
@Output() formBuilt = new EventEmitter<FormGroup>()
|
||||
|
||||
|
@ -49,6 +50,10 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit {
|
|||
.subscribe(([ oldValue, newValue ]) => this.onDisplayNameChange(oldValue, newValue))
|
||||
}
|
||||
|
||||
getMinPasswordLengthMessage () {
|
||||
return USER_PASSWORD_VALIDATOR.MESSAGES.minlength
|
||||
}
|
||||
|
||||
private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) {
|
||||
const username = this.form.value['username'] || ''
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
@use '_variables' as *;
|
||||
@use '_mixins' as *;
|
||||
|
||||
input:not([type=submit]) {
|
||||
@include peertube-input-text(100%);
|
||||
display: block;
|
||||
|
||||
&#username,
|
||||
&#name {
|
||||
width: auto !important;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=submit],
|
||||
button {
|
||||
@include peertube-button;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 18px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-bottom: 30px;
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
Verify account email confirmation
|
||||
</div>
|
||||
|
||||
<my-signup-success i18n *ngIf="!isPendingEmail && success" message="Your email has been verified and you may now login.">
|
||||
<my-signup-success i18n *ngIf="!isPendingEmail && success" [requiresEmailVerification]="false">
|
||||
</my-signup-success>
|
||||
|
||||
<div i18n class="alert alert-success" *ngIf="isPendingEmail && success">
|
||||
|
|
|
@ -3,6 +3,8 @@ import { SharedFormModule } from '@app/shared/shared-forms'
|
|||
import { SharedGlobalIconModule } from '@app/shared/shared-icons'
|
||||
import { SharedMainModule } from '@app/shared/shared-main'
|
||||
import { SharedUsersModule } from '@app/shared/shared-users'
|
||||
import { SignupMascotComponent } from './signup-mascot.component'
|
||||
import { SignupStepTitleComponent } from './signup-step-title.component'
|
||||
import { SignupSuccessComponent } from './signup-success.component'
|
||||
|
||||
@NgModule({
|
||||
|
@ -14,7 +16,9 @@ import { SignupSuccessComponent } from './signup-success.component'
|
|||
],
|
||||
|
||||
declarations: [
|
||||
SignupSuccessComponent
|
||||
SignupSuccessComponent,
|
||||
SignupStepTitleComponent,
|
||||
SignupMascotComponent
|
||||
],
|
||||
|
||||
exports: [
|
||||
|
@ -22,7 +26,9 @@ import { SignupSuccessComponent } from './signup-success.component'
|
|||
SharedFormModule,
|
||||
SharedGlobalIconModule,
|
||||
|
||||
SignupSuccessComponent
|
||||
SignupSuccessComponent,
|
||||
SignupStepTitleComponent,
|
||||
SignupMascotComponent
|
||||
],
|
||||
|
||||
providers: [
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
@use '_variables' as *;
|
||||
@use '_mixins' as *;
|
||||
|
||||
.root {
|
||||
display: inline-block;
|
||||
width: 270px;
|
||||
}
|
||||
|
||||
div ::ng-deep svg {
|
||||
color: pvar(--mainColor);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
import { DomSanitizer } from '@angular/platform-browser'
|
||||
|
||||
const images = {
|
||||
about: require('!!raw-loader?!../../../assets/images/mascot/register/about.svg').default,
|
||||
terms: require('!!raw-loader?!../../../assets/images/mascot/register/terms.svg').default,
|
||||
success: require('!!raw-loader?!../../../assets/images/mascot/register/success.svg').default,
|
||||
channel: require('!!raw-loader?!../../../assets/images/mascot/register/channel.svg').default,
|
||||
account: require('!!raw-loader?!../../../assets/images/mascot/register/account.svg').default
|
||||
}
|
||||
|
||||
export type MascotImageName = keyof typeof images
|
||||
|
||||
@Component({
|
||||
selector: 'my-signup-mascot',
|
||||
styleUrls: [ './signup-mascot.component.scss' ],
|
||||
template: `<div class="root" [innerHTML]="html"></div>`
|
||||
})
|
||||
export class SignupMascotComponent {
|
||||
@Input() imageName: MascotImageName
|
||||
|
||||
constructor (private sanitize: DomSanitizer) {
|
||||
|
||||
}
|
||||
|
||||
get html () {
|
||||
return this.sanitize.bypassSecurityTrustHtml(images[this.imageName])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<div class="step-content-title">
|
||||
<my-signup-mascot [imageName]="mascotImageName"></my-signup-mascot>
|
||||
|
||||
<h2>
|
||||
<ng-content></ng-content>
|
||||
</h2>
|
||||
|
||||
<div class="step-content-title-separator"></div>
|
||||
</div>
|
|
@ -0,0 +1,23 @@
|
|||
@use '_variables' as *;
|
||||
@use '_mixins' as *;
|
||||
|
||||
.step-content-title {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
margin-bottom: 45px;
|
||||
|
||||
h2 {
|
||||
font-size: 32px;
|
||||
font-weight: normal;
|
||||
max-width: 300px;
|
||||
margin: 15px auto 0;
|
||||
}
|
||||
}
|
||||
|
||||
.step-content-title-separator {
|
||||
height: 6px;
|
||||
width: 60px;
|
||||
border-radius: 4px;
|
||||
background-color: pvar(--mainColor);
|
||||
margin: 5px auto 0;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
import { MascotImageName } from './signup-mascot.component'
|
||||
|
||||
@Component({
|
||||
selector: 'my-signup-step-title',
|
||||
templateUrl: './signup-step-title.component.html',
|
||||
styleUrls: [ './signup-step-title.component.scss' ]
|
||||
})
|
||||
export class SignupStepTitleComponent {
|
||||
@Input() mascotImageName: MascotImageName
|
||||
|
||||
}
|
|
@ -1,20 +1,22 @@
|
|||
<!-- Thanks: Amit Singh Sansoya from https://codepen.io/amit3200/pen/zWMJOO -->
|
||||
<my-signup-step-title mascotImageName="success" i18n>
|
||||
<strong>Welcome</strong>
|
||||
<div>on {{ instanceName }}</div>
|
||||
</my-signup-step-title>
|
||||
|
||||
<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>
|
||||
<div class="alert pt-alert-primary">
|
||||
<p i18n>Your account has been created!</p>
|
||||
|
||||
<p i18n class="bottom-message">Welcome to PeerTube!</p>
|
||||
|
||||
<div *ngIf="message" class="alert alert-success">
|
||||
<p>{{ message }}</p>
|
||||
|
||||
<p i18n>
|
||||
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>.
|
||||
<p i18n *ngIf="requiresEmailVerification">
|
||||
<strong>Check your emails</strong> to validate your account and complete your inscription.
|
||||
</p>
|
||||
|
||||
<p i18n>
|
||||
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>.
|
||||
</p>
|
||||
<ng-container *ngIf="!requiresEmailVerification">
|
||||
<p i18n>
|
||||
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>.
|
||||
</p>
|
||||
|
||||
<p i18n>
|
||||
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>.
|
||||
</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 957 B |
|
@ -1,54 +1,6 @@
|
|||
svg {
|
||||
width: 100px;
|
||||
display: block;
|
||||
margin: 40px auto 0;
|
||||
}
|
||||
|
||||
.path {
|
||||
stroke-dasharray: 1000;
|
||||
stroke-dashoffset: 0;
|
||||
|
||||
&.circle {
|
||||
animation: dash .9s ease-in-out;
|
||||
}
|
||||
|
||||
&.line {
|
||||
stroke-dashoffset: 1000;
|
||||
animation: dash .9s .35s ease-in-out forwards;
|
||||
}
|
||||
|
||||
&.check {
|
||||
stroke-dashoffset: -100;
|
||||
animation: dash-check .9s .35s ease-in-out forwards;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-message {
|
||||
text-align: center;
|
||||
margin: 20px 0 60px;
|
||||
font-size: 1.25em;
|
||||
color: #73AF55;
|
||||
}
|
||||
|
||||
.alert {
|
||||
font-size: 15px;
|
||||
font-size: 18px;
|
||||
max-width: 900px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@keyframes dash {
|
||||
0% {
|
||||
stroke-dashoffset: 1000;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dash-check {
|
||||
0% {
|
||||
stroke-dashoffset: -100;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 900;
|
||||
}
|
||||
margin: auto;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
import { ServerService } from '@app/core'
|
||||
|
||||
@Component({
|
||||
selector: 'my-signup-success',
|
||||
|
@ -6,5 +7,13 @@ import { Component, Input } from '@angular/core'
|
|||
styleUrls: [ './signup-success.component.scss' ]
|
||||
})
|
||||
export class SignupSuccessComponent {
|
||||
@Input() message: string
|
||||
@Input() requiresEmailVerification: boolean
|
||||
|
||||
constructor (private serverService: ServerService) {
|
||||
|
||||
}
|
||||
|
||||
get instanceName () {
|
||||
return this.serverService.getHTMLConfig().instance.name
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ export const USER_EXISTING_PASSWORD_VALIDATOR: BuildFormValidator = {
|
|||
}
|
||||
}
|
||||
|
||||
export const USER_PASSWORD_VALIDATOR: BuildFormValidator = {
|
||||
export const USER_PASSWORD_VALIDATOR = {
|
||||
VALIDATORS: [
|
||||
Validators.required,
|
||||
Validators.minLength(6),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<h2 class="instance-name">{{ about?.instance.name }}</h2>
|
||||
<h2 *ngIf="displayInstanceName" class="instance-name">{{ about?.instance.name }}</h2>
|
||||
|
||||
<div class="instance-short-description">{{ about?.instance.shortDescription }}</div>
|
||||
<div *ngIf="displayInstanceShortDescription" class="instance-short-description">{{ about?.instance.shortDescription }}</div>
|
||||
|
||||
<ngb-accordion #accordion="ngbAccordion" [closeOthers]="true">
|
||||
<ngb-panel *ngIf="panels.features" id="instance-features" i18n-title title="Features found on this instance">
|
||||
|
@ -32,7 +32,7 @@
|
|||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="termsPanel" id="terms" i18n-title title="Terms">
|
||||
<ngb-panel *ngIf="termsPanel" id="terms" [title]="getTermsTitle()">
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="block" [innerHTML]="aboutHtml.terms"></div>
|
||||
</ng-template>
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
.instance-short-description {
|
||||
@include ellipsis-multiline(1rem, 3);
|
||||
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
margin: 25px 0;
|
||||
}
|
||||
|
||||
.block {
|
||||
|
|
|
@ -15,6 +15,9 @@ export class InstanceAboutAccordionComponent implements OnInit {
|
|||
|
||||
@Output() init: EventEmitter<InstanceAboutAccordionComponent> = new EventEmitter<InstanceAboutAccordionComponent>()
|
||||
|
||||
@Input() displayInstanceName = true
|
||||
@Input() displayInstanceShortDescription = true
|
||||
|
||||
@Input() pluginScope: PluginClientScope
|
||||
@Input() pluginHook: ClientFilterHookName
|
||||
|
||||
|
@ -66,6 +69,10 @@ export class InstanceAboutAccordionComponent implements OnInit {
|
|||
return !!(this.aboutHtml?.administrator || this.about?.instance.maintenanceLifetime || this.about?.instance.businessModel)
|
||||
}
|
||||
|
||||
getTermsTitle () {
|
||||
return $localize`Terms of ${this.about.instance.name}`
|
||||
}
|
||||
|
||||
get moderationPanel () {
|
||||
return this.panels.moderation && !!this.aboutHtml.moderationInformation
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 40 KiB |
|
@ -26,6 +26,7 @@ body {
|
|||
--mainColor: #{$main-color};
|
||||
--mainColorLighter: #{$main-color-lighter};
|
||||
--mainColorLightest: #{$main-color-lightest};
|
||||
--mainColorVeryLight: #{$main-color-very-light};
|
||||
|
||||
--mainHoverColor: #{$main-hover-color};
|
||||
--mainBackgroundHoverColor: #{$main-background-hover-color};
|
||||
|
|
|
@ -3,6 +3,24 @@
|
|||
@use '_badges' as *;
|
||||
@use '_icons' as *;
|
||||
|
||||
.link-orange {
|
||||
color: pvar(--mainForegroundColor);
|
||||
font-weight: $font-bold;
|
||||
border-bottom: 3px solid pvar(--mainColor);
|
||||
|
||||
&:hover {
|
||||
color: pvar(--mainForegroundColor);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.underline-orange {
|
||||
display: inline-block;
|
||||
border-bottom: 3px solid pvar(--mainColor);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
.peertube-button {
|
||||
@include peertube-button;
|
||||
}
|
||||
|
@ -70,6 +88,11 @@
|
|||
margin-top: 10px;
|
||||
}
|
||||
|
||||
label + .form-group-description {
|
||||
margin-bottom: 10px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
@ -192,9 +215,12 @@
|
|||
border-left-width: .25rem;
|
||||
}
|
||||
|
||||
&.callout-info {
|
||||
&.callout-orange {
|
||||
border-color: pvar(--mainColorLightest);
|
||||
border-left-color: pvar(--mainColor);
|
||||
|
||||
&:not(.callout-light) {
|
||||
border-left-color: pvar(--mainColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,3 +236,16 @@
|
|||
top: #{-($header-height + $sub-menu-height + 20px)};
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
.alert {
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.pt-alert-primary {
|
||||
background-color: pvar(--mainColorVeryLight);
|
||||
border: 2px solid pvar(--mainColorLightest);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,5 +46,5 @@ $dropdown-border-radius: 3px;
|
|||
$dropdown-link-active-color: pvar(--mainForegroundColor);
|
||||
$dropdown-link-active-bg: pvar(--mainBackgroundHoverColor);
|
||||
|
||||
$accordion-button-active-bg: pvar(--mainColorLightest);
|
||||
$accordion-button-active-bg: pvar(--mainColorVeryLight);
|
||||
$accordion-button-active-color: pvar(--mainForegroundColor);
|
||||
|
|
|
@ -264,6 +264,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
@mixin peertube-button-big {
|
||||
height: auto;
|
||||
padding: 10px 25px;
|
||||
font-size: 18px;
|
||||
line-height: 1.2;
|
||||
border: 0;
|
||||
font-weight: $font-semibold;
|
||||
|
||||
// Because of primeng that redefines border-radius of all input[type="..."]
|
||||
border-radius: 3px !important;
|
||||
}
|
||||
|
||||
@mixin peertube-button-link {
|
||||
@include disable-default-a-behaviour;
|
||||
@include peertube-button;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@use 'sass:math';
|
||||
@use 'sass:color';
|
||||
@use '~bootstrap/scss/functions' as *;
|
||||
|
||||
$small-view: 800px;
|
||||
|
@ -14,11 +15,12 @@ $grey-background-color: #E5E5E5;
|
|||
$grey-background-hover-color: #EFEFEF;
|
||||
$grey-foreground-color: #585858;
|
||||
$grey-foreground-hover-color: #303030;
|
||||
$grey-button-outline-color: scale-color($grey-foreground-color, $alpha: -95%);
|
||||
$grey-button-outline-color: color.scale($grey-foreground-color, $alpha: -95%);
|
||||
|
||||
$main-color: hsl(24, 90%, 50%);
|
||||
$main-color-lighter: lighten($main-color, 10%);
|
||||
$main-color-lightest: lighten($main-color, 40%);
|
||||
$main-color-very-light: #fff5eb;
|
||||
|
||||
$main-hover-color: lighten($main-color, 5%);
|
||||
$main-background-hover-color: #e9ecef;
|
||||
|
@ -109,6 +111,7 @@ $variables: (
|
|||
--mainColor: var(--mainColor),
|
||||
--mainColorLighter: var(--mainColorLighter),
|
||||
--mainColorLightest: var(--mainColorLightest),
|
||||
--mainColorVeryLight: var(--mainColorVeryLight),
|
||||
|
||||
--mainHoverColor: var(--mainHoverColor),
|
||||
--mainBackgroundHoverColor: var(--mainBackgroundHoverColor),
|
||||
|
|