mirror of https://github.com/Chocobozzz/PeerTube
improvements to login and sign-up pages (#3357)
* New login form ui * Move InstanceAboutAccordion to shared components * Update closed registration alert text * Add alert for opened registration and move them bellow login form * Adjust flex block on signup and login views * Replace toggle accordion with expand on links in signup and login + scrollTo * Improve display of login alerts * Fix missing Component suffix * Define min-width instance-information block sign-up and login for mobile screens * Add ability to select specific panels in instanceAboutAccorddion * Add instance title and short-description to common instanceAboutAccordion * Clarify title alert in login page * Add step terms for signup Co-authored-by: kimsible <kimsible@users.noreply.github.com> Co-authored-by: Rigel Kent <sendmemail@rigelk.eu>pull/3423/head
parent
10f26f4203
commit
40360c17d8
|
@ -8,73 +8,81 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-container *ngIf="!externalAuthError && !isAuthenticatedWithExternalAuth">
|
<ng-container *ngIf="!externalAuthError && !isAuthenticatedWithExternalAuth">
|
||||||
<div class="looking-for-account alert alert-info" *ngIf="signupAllowed === false" role="alert">
|
|
||||||
<h6 class="alert-heading" i18n>
|
|
||||||
If you are looking for an account…
|
|
||||||
</h6>
|
|
||||||
|
|
||||||
<div i18n>
|
|
||||||
Currently this instance doesn't allow for user registration, but you can find an instance
|
|
||||||
that gives you the possibility to sign up for an account and upload your videos there.
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
Find yours among multiple instances at <a class="alert-link" href="https://joinpeertube.org/instances" target="_blank" rel="noopener noreferrer">https://joinpeertube.org/instances</a>.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="error" class="alert alert-danger">{{ error }}
|
<div *ngIf="error" class="alert alert-danger">{{ error }}
|
||||||
<span *ngIf="error === 'User email is not verified.'"> <a i18n routerLink="/verify-account/ask-send-email">Request new verification email.</a></span>
|
<span *ngIf="error === 'User email is not verified.'"> <a i18n routerLink="/verify-account/ask-send-email">Request new verification email.</a></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="login-form-and-externals">
|
<div class="wrapper">
|
||||||
|
<div class="login-form-and-externals">
|
||||||
|
|
||||||
<form role="form" (ngSubmit)="login()" [formGroup]="form">
|
<form role="form" (ngSubmit)="login()" [formGroup]="form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<label i18n for="username">User</label>
|
<label i18n for="username">User</label>
|
||||||
<input
|
<input
|
||||||
type="text" id="username" i18n-placeholder placeholder="Username or email address" required tabindex="1"
|
type="text" id="username" i18n-placeholder placeholder="Username or email address" required tabindex="1"
|
||||||
formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" #usernameInput
|
formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" #usernameInput
|
||||||
>
|
>
|
||||||
<a i18n *ngIf="signupAllowed === true" routerLink="/signup" class="create-an-account">
|
</div>
|
||||||
or create an account
|
|
||||||
</a>
|
<div *ngIf="formErrors.username" class="form-error">
|
||||||
|
{{ formErrors.username }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="formErrors.username" class="form-error">
|
<div class="form-group">
|
||||||
{{ formErrors.username }}
|
<label i18n for="password">Password</label>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label i18n for="password">Password</label>
|
|
||||||
<div>
|
|
||||||
<my-input-toggle-hidden formControlName="password" id="password"
|
<my-input-toggle-hidden formControlName="password" id="password"
|
||||||
i18n-placeholder placeholder="Password"
|
i18n-placeholder placeholder="Password"
|
||||||
[ngClass]="{ 'input-error': formErrors['password'] }"
|
[ngClass]="{ 'input-error': formErrors['password'] }"
|
||||||
autocomplete="current-password"></my-input-toggle-hidden>
|
autocomplete="current-password" tabindex="2"></my-input-toggle-hidden>
|
||||||
<a i18n-title class="forgot-password-button" (click)="openForgotPasswordModal()" title="Click here to reset your password">I forgot my password</a>
|
<div *ngIf="formErrors.password" class="form-error">
|
||||||
|
{{ formErrors.password }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="formErrors.password" class="form-error">
|
|
||||||
{{ formErrors.password }}
|
<input type="submit" i18n-value value="Login" [disabled]="!form.valid">
|
||||||
|
|
||||||
|
<div class="additionnal-links">
|
||||||
|
<a class="forgot-password-button" (click)="openForgotPasswordModal()" i18n-title title="Click here to reset your password">I forgot my password</a>
|
||||||
|
<div *ngIf="signupAllowed" class="signup-link">
|
||||||
|
<span>·</span>
|
||||||
|
<a i18n routerLink="/signup" class="create-an-account">Create an account</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="submit" i18n-value value="Login" [disabled]="!form.valid">
|
<div class="looking-for-account alert alert-info" role="alert">
|
||||||
</form>
|
<h6 class="alert-heading" i18n>
|
||||||
|
Logging into an account lets you publish content
|
||||||
|
</h6>
|
||||||
|
|
||||||
<div class="external-login-blocks" *ngIf="getExternalLogins().length !== 0">
|
<div *ngIf="signupAllowed" i18n>
|
||||||
<div class="block-title" i18n>Or sign in with</div>
|
This instance allows registration. However, be careful to check the <a class="terms-anchor" (click)="onTermsClick($event, instanceInformation)" href='#'>Terms</a><a class="terms-link" target="_blank" routerLink="/about/instance" fragment="terms">Terms</a> before creating an account.
|
||||||
|
You may also search for another instance to match your exact needs at: <br /><a class="alert-link" href="https://joinpeertube.org/instances" target="_blank" rel="noopener noreferrer">https://joinpeertube.org/instances</a>.
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div *ngIf="!signupAllowed" i18n>
|
||||||
<a class="external-login-block" *ngFor="let auth of getExternalLogins()" [href]="getAuthHref(auth)" role="button">
|
Currently this instance doesn't allow for user registration, you may check the <a (click)="onTermsClick($event, instanceInformation)" href='#'>Terms</a> for more details or find an instance that gives you the possibility to sign up for an account and upload your videos there.
|
||||||
{{ auth.authDisplayName }}
|
Find yours among multiple instances at: <br /> <a class="alert-link" href="https://joinpeertube.org/instances" target="_blank" rel="noopener noreferrer">https://joinpeertube.org/instances</a>.
|
||||||
</a>
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="external-login-blocks" *ngIf="getExternalLogins().length !== 0">
|
||||||
|
<div class="block-title" i18n>Or sign in with</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a class="external-login-block" *ngFor="let auth of getExternalLogins()" [href]="getAuthHref(auth)" role="button">
|
||||||
|
{{ auth.authDisplayName }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div #instanceInformation class="instance-information">
|
||||||
|
<my-instance-about-accordion (init)="onInstanceAboutAccordionInit($event)" [panels]="instanceInformationPanels"></my-instance-about-accordion>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
@import '_variables';
|
@import '_variables';
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
|
@import './_bootstrap-variables';
|
||||||
|
@import '~bootstrap/scss/functions';
|
||||||
|
@import '~bootstrap/scss/variables';
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -57,39 +60,138 @@ input[type=submit] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-form-and-externals {
|
.wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
font-size: 15px;
|
|
||||||
|
|
||||||
form {
|
& > div {
|
||||||
margin: 0 50px 20px 0;
|
flex: 1 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.external-login-blocks {
|
.login-form-and-externals {
|
||||||
min-width: 200px;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
font-size: 15px;
|
||||||
|
max-width: 450px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
.block-title {
|
form {
|
||||||
font-weight: $font-semibold;
|
margin: 0;
|
||||||
|
|
||||||
|
&, input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.additionnal-links {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.forgot-password-button,
|
||||||
|
.create-an-account {
|
||||||
|
padding: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
color: var(--mainColor);
|
||||||
|
|
||||||
|
&:hover, &:active {
|
||||||
|
color: var(--mainHoverColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.external-login-block {
|
.external-login-blocks {
|
||||||
@include disable-default-a-behaviour;
|
min-width: 200px;
|
||||||
|
|
||||||
cursor: pointer;
|
.block-title {
|
||||||
border: 1px solid #d1d7e0;
|
font-weight: $font-semibold;
|
||||||
border-radius: 5px;
|
|
||||||
color: pvar(--mainForegroundColor);
|
|
||||||
margin: 10px 10px 0 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 35px;
|
|
||||||
min-width: 100px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: rgba(209, 215, 224, 0.5)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.external-login-block {
|
||||||
|
@include disable-default-a-behaviour;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid #d1d7e0;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: pvar(--mainForegroundColor);
|
||||||
|
margin: 10px 10px 0 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 35px;
|
||||||
|
min-width: 100px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(209, 215, 224, 0.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup-link {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.instance-information {
|
||||||
|
max-width: 600px;
|
||||||
|
min-width: 350px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terms-anchor {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terms-link {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin columnReverseDisplay {
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
|
||||||
|
.login-form-and-externals,
|
||||||
|
.instance-information {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
max-width: 450px;
|
||||||
|
min-width: unset;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instance-information {
|
||||||
|
::ng-deep .accordion {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.terms-anchor {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terms-link {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: breakpoint(md)) {
|
||||||
|
.wrapper {
|
||||||
|
@include columnReverseDisplay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: breakpoint(md) + $menu-width) {
|
||||||
|
:host-context(.main-col:not(.expanded)) {
|
||||||
|
.wrapper {
|
||||||
|
@include columnReverseDisplay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angula
|
||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
import { AuthService, Notifier, RedirectService, UserService } from '@app/core'
|
import { AuthService, Notifier, RedirectService, UserService } from '@app/core'
|
||||||
import { HooksService } from '@app/core/plugins/hooks.service'
|
import { HooksService } from '@app/core/plugins/hooks.service'
|
||||||
|
import { InstanceAboutAccordionComponent } from '@app/shared/shared-instance'
|
||||||
import { LOGIN_PASSWORD_VALIDATOR, LOGIN_USERNAME_VALIDATOR } from '@app/shared/form-validators/login-validators'
|
import { LOGIN_PASSWORD_VALIDATOR, LOGIN_USERNAME_VALIDATOR } from '@app/shared/form-validators/login-validators'
|
||||||
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||||
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbAccordion, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { RegisteredExternalAuthConfig, ServerConfig } from '@shared/models'
|
import { RegisteredExternalAuthConfig, ServerConfig } from '@shared/models'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -18,6 +19,7 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
|
||||||
@ViewChild('usernameInput', { static: false }) usernameInput: ElementRef
|
@ViewChild('usernameInput', { static: false }) usernameInput: ElementRef
|
||||||
@ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef
|
@ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef
|
||||||
|
|
||||||
|
accordion: NgbAccordion
|
||||||
error: string = null
|
error: string = null
|
||||||
forgotPasswordEmail = ''
|
forgotPasswordEmail = ''
|
||||||
|
|
||||||
|
@ -25,6 +27,14 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
|
||||||
externalAuthError = false
|
externalAuthError = false
|
||||||
externalLogins: string[] = []
|
externalLogins: string[] = []
|
||||||
|
|
||||||
|
instanceInformationPanels = {
|
||||||
|
terms: true,
|
||||||
|
administrators: false,
|
||||||
|
features: false,
|
||||||
|
moderation: false,
|
||||||
|
codeOfConduct: false
|
||||||
|
}
|
||||||
|
|
||||||
private openedForgotPasswordModal: NgbModalRef
|
private openedForgotPasswordModal: NgbModalRef
|
||||||
private serverConfig: ServerConfig
|
private serverConfig: ServerConfig
|
||||||
|
|
||||||
|
@ -45,6 +55,15 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
|
||||||
return this.serverConfig.signup.allowed === true
|
return this.serverConfig.signup.allowed === true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTermsClick (event: Event, instanceInformation: HTMLElement) {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
if (this.accordion) {
|
||||||
|
this.accordion.expand('terms')
|
||||||
|
instanceInformation.scrollIntoView({ behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isEmailDisabled () {
|
isEmailDisabled () {
|
||||||
return this.serverConfig.email.enabled === false
|
return this.serverConfig.email.enabled === false
|
||||||
}
|
}
|
||||||
|
@ -122,6 +141,10 @@ The link will expire within 1 hour.`
|
||||||
this.openedForgotPasswordModal.close()
|
this.openedForgotPasswordModal.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onInstanceAboutAccordionInit (instanceAboutAccordion: InstanceAboutAccordionComponent) {
|
||||||
|
this.accordion = instanceAboutAccordion.accordion
|
||||||
|
}
|
||||||
|
|
||||||
private loadExternalAuthToken (username: string, token: string) {
|
private loadExternalAuthToken (username: string, token: string) {
|
||||||
this.isAuthenticatedWithExternalAuth = true
|
this.isAuthenticatedWithExternalAuth = true
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { SharedFormModule } from '@app/shared/shared-forms'
|
import { SharedFormModule } from '@app/shared/shared-forms'
|
||||||
import { SharedGlobalIconModule } from '@app/shared/shared-icons'
|
import { SharedGlobalIconModule } from '@app/shared/shared-icons'
|
||||||
|
import { SharedInstanceModule } from '@app/shared/shared-instance'
|
||||||
import { SharedMainModule } from '@app/shared/shared-main'
|
import { SharedMainModule } from '@app/shared/shared-main'
|
||||||
import { LoginRoutingModule } from './login-routing.module'
|
import { LoginRoutingModule } from './login-routing.module'
|
||||||
import { LoginComponent } from './login.component'
|
import { LoginComponent } from './login.component'
|
||||||
|
@ -11,7 +12,9 @@ import { LoginComponent } from './login.component'
|
||||||
|
|
||||||
SharedMainModule,
|
SharedMainModule,
|
||||||
SharedFormModule,
|
SharedFormModule,
|
||||||
SharedGlobalIconModule
|
SharedGlobalIconModule,
|
||||||
|
|
||||||
|
SharedInstanceModule
|
||||||
],
|
],
|
||||||
|
|
||||||
declarations: [
|
declarations: [
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<header *ngIf="steps.length > 2">
|
<header *ngIf="steps.length > 2">
|
||||||
<ng-container *ngFor="let step of steps; let i = index; let isLast = last;">
|
<ng-container *ngFor="let step of steps; let i = index; let isLast = last;">
|
||||||
<div
|
<div
|
||||||
class="step-info" [ngClass]="{ active: selectedIndex === i, completed: isCompleted(step) }" [attr.aria-current]="selectedIndex === i"
|
class="step-info" [ngClass]="{ active: selectedIndex === i, completed: isCompleted(step), 'c-hand': isAccessible(i) }" [attr.aria-current]="selectedIndex === i"
|
||||||
(click)="onClick(i)"
|
(click)="onClick(i)"
|
||||||
>
|
>
|
||||||
<div class="step-index">
|
<div class="step-index">
|
||||||
|
|
|
@ -4,6 +4,12 @@
|
||||||
$grey-color: #9CA3AB;
|
$grey-color: #9CA3AB;
|
||||||
$index-block-height: 32px;
|
$index-block-height: 32px;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
max-width: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@ -17,6 +23,10 @@ header {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: $index-block-height;
|
width: $index-block-height;
|
||||||
|
|
||||||
|
&:not(.c-hand) {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
.step-index {
|
.step-index {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
@ -16,4 +16,11 @@ export class CustomStepperComponent extends CdkStepper {
|
||||||
isCompleted (step: CdkStep) {
|
isCompleted (step: CdkStep) {
|
||||||
return step.stepControl && step.stepControl.dirty && step.stepControl.valid
|
return step.stepControl && step.stepControl.dirty && step.stepControl.valid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isAccessible (index: number) {
|
||||||
|
const stepsCompletedMap = this.steps.map(step => this.isCompleted(step))
|
||||||
|
return index === 0
|
||||||
|
? true
|
||||||
|
: stepsCompletedMap[ index - 1 ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<form role="form" [formGroup]="form">
|
||||||
|
<div class="form-group form-group-terms">
|
||||||
|
<my-peertube-checkbox inputName="terms" formControlName="terms">
|
||||||
|
<ng-template ptTemplate="label">
|
||||||
|
<ng-container i18n>
|
||||||
|
I am at least 16 years old and agree
|
||||||
|
to the <a class="terms-anchor" (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>
|
||||||
|
</form>
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
|
||||||
|
import { FormGroup } from '@angular/forms'
|
||||||
|
import {
|
||||||
|
USER_TERMS_VALIDATOR
|
||||||
|
} from '@app/shared/form-validators/user-validators'
|
||||||
|
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-register-step-terms',
|
||||||
|
templateUrl: './register-step-terms.component.html',
|
||||||
|
styleUrls: [ './register.component.scss' ]
|
||||||
|
})
|
||||||
|
export class RegisterStepTermsComponent extends FormReactive implements OnInit {
|
||||||
|
@Input() hasCodeOfConduct = false
|
||||||
|
|
||||||
|
@Output() formBuilt = new EventEmitter<FormGroup>()
|
||||||
|
@Output() termsClick = new EventEmitter<void>()
|
||||||
|
@Output() codeOfConductClick = new EventEmitter<void>()
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
protected formValidatorService: FormValidatorService
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
get instanceHost () {
|
||||||
|
return window.location.host
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
this.buildForm({
|
||||||
|
terms: USER_TERMS_VALIDATOR
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => this.formBuilt.emit(this.form))
|
||||||
|
}
|
||||||
|
|
||||||
|
onTermsClick (event: Event) {
|
||||||
|
event.preventDefault()
|
||||||
|
this.termsClick.emit()
|
||||||
|
}
|
||||||
|
|
||||||
|
onCodeOfConductClick (event: Event) {
|
||||||
|
event.preventDefault()
|
||||||
|
this.codeOfConductClick.emit()
|
||||||
|
}
|
||||||
|
}
|
|
@ -63,20 +63,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group form-group-terms">
|
|
||||||
<my-peertube-checkbox inputName="terms" formControlName="terms">
|
|
||||||
<ng-template ptTemplate="label">
|
|
||||||
<ng-container i18n>
|
|
||||||
I am at least 16 years old and agree
|
|
||||||
to the <a (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>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
USER_DISPLAY_NAME_REQUIRED_VALIDATOR,
|
USER_DISPLAY_NAME_REQUIRED_VALIDATOR,
|
||||||
USER_EMAIL_VALIDATOR,
|
USER_EMAIL_VALIDATOR,
|
||||||
USER_PASSWORD_VALIDATOR,
|
USER_PASSWORD_VALIDATOR,
|
||||||
USER_TERMS_VALIDATOR,
|
|
||||||
USER_USERNAME_VALIDATOR
|
USER_USERNAME_VALIDATOR
|
||||||
} from '@app/shared/form-validators/user-validators'
|
} from '@app/shared/form-validators/user-validators'
|
||||||
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||||
|
@ -18,12 +17,9 @@ import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||||
styleUrls: [ './register.component.scss' ]
|
styleUrls: [ './register.component.scss' ]
|
||||||
})
|
})
|
||||||
export class RegisterStepUserComponent extends FormReactive implements OnInit {
|
export class RegisterStepUserComponent extends FormReactive implements OnInit {
|
||||||
@Input() hasCodeOfConduct = false
|
|
||||||
@Input() videoUploadDisabled = false
|
@Input() videoUploadDisabled = false
|
||||||
|
|
||||||
@Output() formBuilt = new EventEmitter<FormGroup>()
|
@Output() formBuilt = new EventEmitter<FormGroup>()
|
||||||
@Output() termsClick = new EventEmitter<void>()
|
|
||||||
@Output() codeOfConductClick = new EventEmitter<void>()
|
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
protected formValidatorService: FormValidatorService,
|
protected formValidatorService: FormValidatorService,
|
||||||
|
@ -41,8 +37,7 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit {
|
||||||
displayName: USER_DISPLAY_NAME_REQUIRED_VALIDATOR,
|
displayName: USER_DISPLAY_NAME_REQUIRED_VALIDATOR,
|
||||||
username: USER_USERNAME_VALIDATOR,
|
username: USER_USERNAME_VALIDATOR,
|
||||||
password: USER_PASSWORD_VALIDATOR,
|
password: USER_PASSWORD_VALIDATOR,
|
||||||
email: USER_EMAIL_VALIDATOR,
|
email: USER_EMAIL_VALIDATOR
|
||||||
terms: USER_TERMS_VALIDATOR
|
|
||||||
})
|
})
|
||||||
|
|
||||||
setTimeout(() => this.formBuilt.emit(this.form))
|
setTimeout(() => this.formBuilt.emit(this.form))
|
||||||
|
@ -54,16 +49,6 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit {
|
||||||
.subscribe(([ oldValue, newValue ]) => this.onDisplayNameChange(oldValue, newValue))
|
.subscribe(([ oldValue, newValue ]) => this.onDisplayNameChange(oldValue, newValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
onTermsClick (event: Event) {
|
|
||||||
event.preventDefault()
|
|
||||||
this.termsClick.emit()
|
|
||||||
}
|
|
||||||
|
|
||||||
onCodeOfConductClick (event: Event) {
|
|
||||||
event.preventDefault()
|
|
||||||
this.codeOfConductClick.emit()
|
|
||||||
}
|
|
||||||
|
|
||||||
private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) {
|
private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) {
|
||||||
const username = this.form.value['username'] || ''
|
const username = this.form.value['username'] || ''
|
||||||
|
|
||||||
|
|
|
@ -10,19 +10,26 @@
|
||||||
<div class="wrapper" [hidden]="signupDone">
|
<div class="wrapper" [hidden]="signupDone">
|
||||||
<div class="register-form">
|
<div class="register-form">
|
||||||
<my-custom-stepper linear *ngIf="!signupDone">
|
<my-custom-stepper linear *ngIf="!signupDone">
|
||||||
<cdk-step [stepControl]="formStepUser" i18n-label label="User">
|
<cdk-step [stepControl]="formStepTerms" i18n-label="Stepper label for the registration page describing terms of service" label="Terms">
|
||||||
<my-register-step-user
|
<div class="instance-information">
|
||||||
[hasCodeOfConduct]="!!aboutHtml.codeOfConduct"
|
<my-instance-about-accordion (init)="onInstanceAboutAccordionInit($event)" [panels]="instanceInformationPanels"></my-instance-about-accordion>
|
||||||
[videoUploadDisabled]="videoUploadDisabled"
|
</div>
|
||||||
(formBuilt)="onUserFormBuilt($event)" (termsClick)="onTermsClick()" (codeOfConductClick)="onCodeOfConductClick()"
|
|
||||||
>
|
|
||||||
</my-register-step-user>
|
|
||||||
|
|
||||||
<button i18n cdkStepperNext [disabled]="!formStepUser || !formStepUser.valid"
|
<my-register-step-terms
|
||||||
(click)="signup()">{{ videoUploadDisabled ? 'Signup' : 'Next' }}</button>
|
[hasCodeOfConduct]="!!aboutHtml.codeOfConduct"
|
||||||
|
(formBuilt)="onTermsFormBuilt($event)" (termsClick)="onTermsClick()" (codeOfConductClick)="onCodeOfConductClick()"
|
||||||
|
></my-register-step-terms>
|
||||||
|
|
||||||
|
<button cdkStepperNext [disabled]="!formStepTerms || !formStepTerms.valid">{{ defaultNextStepButtonLabel }}</button>
|
||||||
</cdk-step>
|
</cdk-step>
|
||||||
|
|
||||||
<cdk-step [stepControl]="formStepChannel" i18n-label label="Channel" *ngIf="!videoUploadDisabled">
|
<cdk-step [stepControl]="formStepUser" i18n-label="Stepper label for the registration page asking user informations" label="User">
|
||||||
|
<my-register-step-user (formBuilt)="onUserFormBuilt($event)" [videoUploadDisabled]="videoUploadDisabled"></my-register-step-user>
|
||||||
|
|
||||||
|
<button cdkStepperNext [disabled]="!formStepUser || !formStepUser.valid" (click)="videoUploadDisabled && signup()">{{ stepUserButtonLabel }}</button>
|
||||||
|
</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>
|
<my-register-step-channel (formBuilt)="onChannelFormBuilt($event)" [username]="getUsername()"></my-register-step-channel>
|
||||||
|
|
||||||
<button i18n cdkStepperNext (click)="signup()"
|
<button i18n cdkStepperNext (click)="signup()"
|
||||||
|
@ -43,58 +50,6 @@
|
||||||
</cdk-step>
|
</cdk-step>
|
||||||
</my-custom-stepper>
|
</my-custom-stepper>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="instance-information">
|
|
||||||
<ngb-accordion [closeOthers]="true" #accordion="ngbAccordion">
|
|
||||||
<ngb-panel id="instance-features" i18n-title title="Features found on this instance">
|
|
||||||
<ng-template ngbPanelContent>
|
|
||||||
<my-instance-features-table></my-instance-features-table>
|
|
||||||
</ng-template>
|
|
||||||
</ngb-panel>
|
|
||||||
|
|
||||||
<ng-container *ngIf="about">
|
|
||||||
<ngb-panel
|
|
||||||
*ngIf="aboutHtml.administrator || about.instance.maintenanceLifetime || about.instance.businessModel"
|
|
||||||
id="admin-sustainability" i18n-title title="Administrators & Sustainability"
|
|
||||||
>
|
|
||||||
<ng-template ngbPanelContent>
|
|
||||||
<div class="block">
|
|
||||||
<strong i18n>Who are we?</strong>
|
|
||||||
<div [innerHTML]="aboutHtml.administrator"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="block">
|
|
||||||
<strong i18n>How long do we plan to maintain this instance?</strong>
|
|
||||||
<div [innerHTML]="about.instance.maintenanceLifetime"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="block">
|
|
||||||
<strong i18n>How will we finance this instance?</strong>
|
|
||||||
<div [innerHTML]="about.instance.businessModel"></div>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</ngb-panel>
|
|
||||||
|
|
||||||
<ngb-panel *ngIf="aboutHtml.moderationInformation" id="moderation-information" i18n-title title="Moderation information">
|
|
||||||
<ng-template ngbPanelContent>
|
|
||||||
<div class="block" [innerHTML]="aboutHtml.moderationInformation"></div>
|
|
||||||
</ng-template>
|
|
||||||
</ngb-panel>
|
|
||||||
|
|
||||||
<ngb-panel *ngIf="aboutHtml.codeOfConduct" id="code-of-conduct" i18n-title title="Code of conduct">
|
|
||||||
<ng-template ngbPanelContent>
|
|
||||||
<div class="block" [innerHTML]="aboutHtml.codeOfConduct"></div>
|
|
||||||
</ng-template>
|
|
||||||
</ngb-panel>
|
|
||||||
|
|
||||||
<ngb-panel *ngIf="aboutHtml.terms" id="terms" i18n-title title="Terms">
|
|
||||||
<ng-template ngbPanelContent>
|
|
||||||
<div class="block" [innerHTML]="aboutHtml.terms"></div>
|
|
||||||
</ng-template>
|
|
||||||
</ngb-panel>
|
|
||||||
</ng-container>
|
|
||||||
</ngb-accordion>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
@import '_variables';
|
@import '_variables';
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
@import "./_bootstrap-variables";
|
|
||||||
|
|
||||||
@import '~bootstrap/scss/functions';
|
|
||||||
@import '~bootstrap/scss/variables';
|
|
||||||
|
|
||||||
.alert {
|
.alert {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
@ -12,44 +8,20 @@
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
flex-direction: column;
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
& > div {
|
.register-form {
|
||||||
margin-bottom: 40px;
|
max-width: 600px;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
&.register-form {
|
.register-form,
|
||||||
width: 450px;
|
.instance-information {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $mobile-view) {
|
.instance-information {
|
||||||
width: 100%;
|
margin-bottom: 15px;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.instance-information {
|
|
||||||
width: 600px;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
|
|
||||||
.block {
|
|
||||||
font-size: 15px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
padding: 0 $btn-padding-x;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 1500px) {
|
|
||||||
width: 450px;
|
|
||||||
}
|
|
||||||
@media screen and (max-width: $mobile-view) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngb-accordion ::ng-deep {
|
|
||||||
.btn {
|
|
||||||
font-weight: $font-semibold !important;
|
|
||||||
color: pvar(--mainForegroundColor) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,15 +30,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group {
|
.input-group {
|
||||||
@include peertube-input-group(400px);
|
@include peertube-input-group(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group-append {
|
.input-group-append {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-group-terms {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
input:not([type=submit]) {
|
input:not([type=submit]) {
|
||||||
@include peertube-input-text(400px);
|
@include peertube-input-text(100%);
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
&#username,
|
&#username,
|
||||||
|
@ -76,19 +52,10 @@ input:not([type=submit]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $mobile-view) {
|
|
||||||
.form-group-terms,
|
|
||||||
.input-group,
|
|
||||||
input:not([type=submit]) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=submit],
|
input[type=submit],
|
||||||
button {
|
button {
|
||||||
@include peertube-button;
|
@include peertube-button;
|
||||||
@include orange-button;
|
@include orange-button;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.name-information {
|
.name-information {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { Component, OnInit, ViewChild } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { FormGroup } from '@angular/forms'
|
import { FormGroup } from '@angular/forms'
|
||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
import { AuthService, Notifier, UserService } from '@app/core'
|
import { AuthService, UserService } from '@app/core'
|
||||||
import { HooksService } from '@app/core/plugins/hooks.service'
|
import { HooksService } from '@app/core/plugins/hooks.service'
|
||||||
import { InstanceService } from '@app/shared/shared-instance'
|
|
||||||
import { NgbAccordion } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbAccordion } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { UserRegister } from '@shared/models'
|
import { UserRegister } from '@shared/models'
|
||||||
import { About, ServerConfig } from '@shared/models/server'
|
import { ServerConfig } from '@shared/models/server'
|
||||||
|
import { InstanceAboutAccordionComponent } from '@app/shared/shared-instance'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-register',
|
selector: 'my-register',
|
||||||
|
@ -14,35 +14,39 @@ import { About, ServerConfig } from '@shared/models/server'
|
||||||
styleUrls: [ './register.component.scss' ]
|
styleUrls: [ './register.component.scss' ]
|
||||||
})
|
})
|
||||||
export class RegisterComponent implements OnInit {
|
export class RegisterComponent implements OnInit {
|
||||||
@ViewChild('accordion', { static: true }) accordion: NgbAccordion
|
accordion: NgbAccordion
|
||||||
|
|
||||||
info: string = null
|
info: string = null
|
||||||
error: string = null
|
error: string = null
|
||||||
success: string = null
|
success: string = null
|
||||||
signupDone = false
|
signupDone = false
|
||||||
|
|
||||||
about: About
|
|
||||||
aboutHtml = {
|
|
||||||
description: '',
|
|
||||||
terms: '',
|
|
||||||
codeOfConduct: '',
|
|
||||||
moderationInformation: '',
|
|
||||||
administrator: ''
|
|
||||||
}
|
|
||||||
|
|
||||||
videoUploadDisabled: boolean
|
videoUploadDisabled: boolean
|
||||||
|
|
||||||
|
formStepTerms: FormGroup
|
||||||
formStepUser: FormGroup
|
formStepUser: FormGroup
|
||||||
formStepChannel: FormGroup
|
formStepChannel: FormGroup
|
||||||
|
|
||||||
|
aboutHtml = {
|
||||||
|
codeOfConduct: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
instanceInformationPanels = {
|
||||||
|
codeOfConduct: true,
|
||||||
|
terms: true,
|
||||||
|
administrators: false,
|
||||||
|
features: false,
|
||||||
|
moderation: false
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultNextStepButtonLabel = $localize`Next`
|
||||||
|
stepUserButtonLabel = this.defaultNextStepButtonLabel
|
||||||
|
|
||||||
private serverConfig: ServerConfig
|
private serverConfig: ServerConfig
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private notifier: Notifier,
|
|
||||||
private userService: UserService,
|
private userService: UserService,
|
||||||
private instanceService: InstanceService,
|
|
||||||
private hooks: HooksService
|
private hooks: HooksService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
@ -55,19 +59,12 @@ export class RegisterComponent implements OnInit {
|
||||||
this.serverConfig = this.route.snapshot.data.serverConfig
|
this.serverConfig = this.route.snapshot.data.serverConfig
|
||||||
|
|
||||||
this.videoUploadDisabled = this.serverConfig.user.videoQuota === 0
|
this.videoUploadDisabled = this.serverConfig.user.videoQuota === 0
|
||||||
|
this.stepUserButtonLabel = this.videoUploadDisabled
|
||||||
this.instanceService.getAbout()
|
? $localize`Signup`
|
||||||
.subscribe(
|
: this.defaultNextStepButtonLabel
|
||||||
async about => {
|
|
||||||
this.about = about
|
|
||||||
|
|
||||||
this.aboutHtml = await this.instanceService.buildHtml(about)
|
|
||||||
},
|
|
||||||
|
|
||||||
err => this.notifier.error(err.message)
|
|
||||||
)
|
|
||||||
|
|
||||||
this.hooks.runAction('action:signup.register.init', 'signup')
|
this.hooks.runAction('action:signup.register.init', 'signup')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hasSameChannelAndAccountNames () {
|
hasSameChannelAndAccountNames () {
|
||||||
|
@ -86,6 +83,10 @@ export class RegisterComponent implements OnInit {
|
||||||
return this.formStepChannel.value['name']
|
return this.formStepChannel.value['name']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTermsFormBuilt (form: FormGroup) {
|
||||||
|
this.formStepTerms = form
|
||||||
|
}
|
||||||
|
|
||||||
onUserFormBuilt (form: FormGroup) {
|
onUserFormBuilt (form: FormGroup) {
|
||||||
this.formStepUser = form
|
this.formStepUser = form
|
||||||
}
|
}
|
||||||
|
@ -102,6 +103,11 @@ export class RegisterComponent implements OnInit {
|
||||||
if (this.accordion) this.accordion.toggle('code-of-conduct')
|
if (this.accordion) this.accordion.toggle('code-of-conduct')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onInstanceAboutAccordionInit (instanceAboutAccordion: InstanceAboutAccordionComponent) {
|
||||||
|
this.accordion = instanceAboutAccordion.accordion
|
||||||
|
this.aboutHtml = instanceAboutAccordion.aboutHtml
|
||||||
|
}
|
||||||
|
|
||||||
async signup () {
|
async signup () {
|
||||||
this.error = null
|
this.error = null
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@ import { CdkStepperModule } from '@angular/cdk/stepper'
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { SignupSharedModule } from '@app/+signup/shared/signup-shared.module'
|
import { SignupSharedModule } from '@app/+signup/shared/signup-shared.module'
|
||||||
import { SharedInstanceModule } from '@app/shared/shared-instance'
|
import { SharedInstanceModule } from '@app/shared/shared-instance'
|
||||||
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { CustomStepperComponent } from './custom-stepper.component'
|
import { CustomStepperComponent } from './custom-stepper.component'
|
||||||
import { RegisterRoutingModule } from './register-routing.module'
|
import { RegisterRoutingModule } from './register-routing.module'
|
||||||
import { RegisterStepChannelComponent } from './register-step-channel.component'
|
import { RegisterStepChannelComponent } from './register-step-channel.component'
|
||||||
|
import { RegisterStepTermsComponent } from './register-step-terms.component'
|
||||||
import { RegisterStepUserComponent } from './register-step-user.component'
|
import { RegisterStepUserComponent } from './register-step-user.component'
|
||||||
import { RegisterComponent } from './register.component'
|
import { RegisterComponent } from './register.component'
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ import { RegisterComponent } from './register.component'
|
||||||
RegisterRoutingModule,
|
RegisterRoutingModule,
|
||||||
|
|
||||||
CdkStepperModule,
|
CdkStepperModule,
|
||||||
NgbAccordionModule,
|
|
||||||
|
|
||||||
SignupSharedModule,
|
SignupSharedModule,
|
||||||
|
|
||||||
|
@ -25,6 +24,7 @@ import { RegisterComponent } from './register.component'
|
||||||
RegisterComponent,
|
RegisterComponent,
|
||||||
CustomStepperComponent,
|
CustomStepperComponent,
|
||||||
RegisterStepChannelComponent,
|
RegisterStepChannelComponent,
|
||||||
|
RegisterStepTermsComponent,
|
||||||
RegisterStepUserComponent
|
RegisterStepUserComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -115,9 +115,7 @@ export const USER_DESCRIPTION_VALIDATOR: BuildFormValidator = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const USER_TERMS_VALIDATOR: BuildFormValidator = {
|
export const USER_TERMS_VALIDATOR: BuildFormValidator = {
|
||||||
VALIDATORS: [
|
VALIDATORS: [ Validators.requiredTrue ],
|
||||||
Validators.requiredTrue
|
|
||||||
],
|
|
||||||
MESSAGES: {
|
MESSAGES: {
|
||||||
'required': $localize`You must agree with the instance terms in order to register on it.`
|
'required': $localize`You must agree with the instance terms in order to register on it.`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export * from './feature-boolean.component'
|
export * from './feature-boolean.component'
|
||||||
|
export * from './instance-about-accordion.component'
|
||||||
export * from './instance-features-table.component'
|
export * from './instance-features-table.component'
|
||||||
export * from './instance-follow.service'
|
export * from './instance-follow.service'
|
||||||
export * from './instance-statistics.component'
|
export * from './instance-statistics.component'
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
<h2 class="instance-name">{{ about?.instance.name }}</h2>
|
||||||
|
|
||||||
|
<div 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">
|
||||||
|
<ng-template ngbPanelContent>
|
||||||
|
<my-instance-features-table></my-instance-features-table>
|
||||||
|
</ng-template>
|
||||||
|
</ngb-panel>
|
||||||
|
|
||||||
|
<ng-container *ngIf="about">
|
||||||
|
<ngb-panel
|
||||||
|
*ngIf="getAdministratorsPanel()"
|
||||||
|
id="admin-sustainability" i18n-title title="Administrators & Sustainability"
|
||||||
|
>
|
||||||
|
<ng-template ngbPanelContent>
|
||||||
|
<div class="block">
|
||||||
|
<strong i18n>Who are we?</strong>
|
||||||
|
<div [innerHTML]="aboutHtml.administrator"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
<strong i18n>How long do we plan to maintain this instance?</strong>
|
||||||
|
<div [innerHTML]="about.instance.maintenanceLifetime"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
<strong i18n>How will we finance this instance?</strong>
|
||||||
|
<div [innerHTML]="about.instance.businessModel"></div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</ngb-panel>
|
||||||
|
|
||||||
|
<ngb-panel *ngIf="termsPanel" id="terms" i18n-title title="Terms">
|
||||||
|
<ng-template ngbPanelContent>
|
||||||
|
<div class="block" [innerHTML]="aboutHtml.terms"></div>
|
||||||
|
</ng-template>
|
||||||
|
</ngb-panel>
|
||||||
|
|
||||||
|
<ngb-panel *ngIf="moderationPanel" id="moderation-information" i18n-title title="Moderation information">
|
||||||
|
<ng-template ngbPanelContent>
|
||||||
|
<div class="block" [innerHTML]="aboutHtml.moderationInformation"></div>
|
||||||
|
</ng-template>
|
||||||
|
</ngb-panel>
|
||||||
|
|
||||||
|
<ngb-panel *ngIf="codeOfConductPanel" id="code-of-conduct" i18n-title title="Code of conduct">
|
||||||
|
<ng-template ngbPanelContent>
|
||||||
|
<div class="block" [innerHTML]="aboutHtml.codeOfConduct"></div>
|
||||||
|
</ng-template>
|
||||||
|
</ngb-panel>
|
||||||
|
</ng-container>
|
||||||
|
</ngb-accordion>
|
|
@ -0,0 +1,46 @@
|
||||||
|
@import '_variables';
|
||||||
|
@import '_mixins';
|
||||||
|
@import "./_bootstrap-variables";
|
||||||
|
|
||||||
|
@import '~bootstrap/scss/functions';
|
||||||
|
@import '~bootstrap/scss/variables';
|
||||||
|
|
||||||
|
.instance-name {
|
||||||
|
line-height: 1.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instance-short-description {
|
||||||
|
@include ellipsis-multiline(1rem, 3);
|
||||||
|
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding: 0 $btn-padding-x;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngb-accordion ::ng-deep {
|
||||||
|
.card {
|
||||||
|
border-color: var(--mainBackgroundColor);
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background-color: unset;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
& + .collapse.show {
|
||||||
|
background-color: var(--submenuColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
@include peertube-button;
|
||||||
|
@include grey-button;
|
||||||
|
|
||||||
|
border-radius: unset;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
|
||||||
|
import { NgbAccordion } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { InstanceService } from './instance.service'
|
||||||
|
import { Notifier } from '@app/core'
|
||||||
|
import { About } from '@shared/models/server'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-instance-about-accordion',
|
||||||
|
templateUrl: './instance-about-accordion.component.html',
|
||||||
|
styleUrls: ['./instance-about-accordion.component.scss']
|
||||||
|
})
|
||||||
|
export class InstanceAboutAccordionComponent implements OnInit {
|
||||||
|
@ViewChild('accordion', { static: true }) accordion: NgbAccordion
|
||||||
|
@Output() init: EventEmitter<InstanceAboutAccordionComponent> = new EventEmitter<InstanceAboutAccordionComponent>()
|
||||||
|
|
||||||
|
@Input() panels = {
|
||||||
|
features: true,
|
||||||
|
administrators: true,
|
||||||
|
moderation: true,
|
||||||
|
codeOfConduct: true,
|
||||||
|
terms: true
|
||||||
|
}
|
||||||
|
|
||||||
|
about: About
|
||||||
|
aboutHtml = {
|
||||||
|
description: '',
|
||||||
|
terms: '',
|
||||||
|
codeOfConduct: '',
|
||||||
|
moderationInformation: '',
|
||||||
|
administrator: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private instanceService: InstanceService,
|
||||||
|
private notifier: Notifier
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit (): void {
|
||||||
|
this.instanceService.getAbout()
|
||||||
|
.subscribe(
|
||||||
|
async about => {
|
||||||
|
this.about = about
|
||||||
|
|
||||||
|
this.aboutHtml = await this.instanceService.buildHtml(about)
|
||||||
|
|
||||||
|
this.init.emit(this)
|
||||||
|
},
|
||||||
|
|
||||||
|
err => this.notifier.error(err.message)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
getAdministratorsPanel () {
|
||||||
|
if (!this.about) return false
|
||||||
|
if (!this.panels.administrators) return false
|
||||||
|
|
||||||
|
return !!(this.aboutHtml?.administrator || this.about?.instance.maintenanceLifetime || this.about?.instance.businessModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
get moderationPanel () {
|
||||||
|
return this.panels.moderation && !!this.aboutHtml.moderationInformation
|
||||||
|
}
|
||||||
|
|
||||||
|
get codeOfConductPanel () {
|
||||||
|
return this.panels.codeOfConduct && !!this.aboutHtml.codeOfConduct
|
||||||
|
}
|
||||||
|
|
||||||
|
get termsPanel () {
|
||||||
|
return this.panels.terms && !!this.aboutHtml.terms
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
|
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
|
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { SharedMainModule } from '../shared-main/shared-main.module'
|
import { SharedMainModule } from '../shared-main/shared-main.module'
|
||||||
import { FeatureBooleanComponent } from './feature-boolean.component'
|
import { FeatureBooleanComponent } from './feature-boolean.component'
|
||||||
|
import { InstanceAboutAccordionComponent } from './instance-about-accordion.component'
|
||||||
import { InstanceFeaturesTableComponent } from './instance-features-table.component'
|
import { InstanceFeaturesTableComponent } from './instance-features-table.component'
|
||||||
import { InstanceFollowService } from './instance-follow.service'
|
import { InstanceFollowService } from './instance-follow.service'
|
||||||
import { InstanceStatisticsComponent } from './instance-statistics.component'
|
import { InstanceStatisticsComponent } from './instance-statistics.component'
|
||||||
|
@ -9,17 +11,20 @@ import { InstanceService } from './instance.service'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
SharedMainModule
|
SharedMainModule,
|
||||||
|
NgbAccordionModule
|
||||||
],
|
],
|
||||||
|
|
||||||
declarations: [
|
declarations: [
|
||||||
FeatureBooleanComponent,
|
FeatureBooleanComponent,
|
||||||
|
InstanceAboutAccordionComponent,
|
||||||
InstanceFeaturesTableComponent,
|
InstanceFeaturesTableComponent,
|
||||||
InstanceStatisticsComponent
|
InstanceStatisticsComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
exports: [
|
exports: [
|
||||||
FeatureBooleanComponent,
|
FeatureBooleanComponent,
|
||||||
|
InstanceAboutAccordionComponent,
|
||||||
InstanceFeaturesTableComponent,
|
InstanceFeaturesTableComponent,
|
||||||
InstanceStatisticsComponent
|
InstanceStatisticsComponent
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue