</div>
<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 }}
<span *ngIf="error === 'User email is not verified.'"> <a i18n routerLink="/verify-account/ask-send-email">Request new verification email.</a></span>
</div>
- <div class="login-form-and-externals">
-
- <form role="form" (ngSubmit)="login()" [formGroup]="form">
- <div class="form-group">
- <div>
- <label i18n for="username">User</label>
- <input
- 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
- >
- <a i18n *ngIf="signupAllowed === true" routerLink="/signup" class="create-an-account">
- or create an account
- </a>
+ <div class="wrapper">
+ <div class="login-form-and-externals">
+
+ <form role="form" (ngSubmit)="login()" [formGroup]="form">
+ <div class="form-group">
+ <div>
+ <label i18n for="username">User</label>
+ <input
+ 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
+ >
+ </div>
+
+ <div *ngIf="formErrors.username" class="form-error">
+ {{ formErrors.username }}
+ </div>
</div>
- <div *ngIf="formErrors.username" class="form-error">
- {{ formErrors.username }}
- </div>
- </div>
-
- <div class="form-group">
- <label i18n for="password">Password</label>
- <div>
+ <div class="form-group">
+ <label i18n for="password">Password</label>
<my-input-toggle-hidden formControlName="password" id="password"
i18n-placeholder placeholder="Password"
[ngClass]="{ 'input-error': formErrors['password'] }"
- autocomplete="current-password"></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>
+ autocomplete="current-password" tabindex="2"></my-input-toggle-hidden>
+ <div *ngIf="formErrors.password" class="form-error">
+ {{ formErrors.password }}
+ </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>
- <input type="submit" i18n-value value="Login" [disabled]="!form.valid">
- </form>
+ <div class="looking-for-account alert alert-info" role="alert">
+ <h6 class="alert-heading" i18n>
+ Logging into an account lets you publish content
+ </h6>
+
+ <div *ngIf="signupAllowed" i18n>
+ 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 *ngIf="!signupAllowed" i18n>
+ 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.
+ 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>.
+ </div>
+ </div>
+ </form>
- <div class="external-login-blocks" *ngIf="getExternalLogins().length !== 0">
- <div class="block-title" i18n>Or sign in with</div>
+ <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>
+ <a class="external-login-block" *ngFor="let auth of getExternalLogins()" [href]="getAuthHref(auth)" role="button">
+ {{ auth.authDisplayName }}
+ </a>
+ </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>
</div>
@import '_variables';
@import '_mixins';
+@import './_bootstrap-variables';
+@import '~bootstrap/scss/functions';
+@import '~bootstrap/scss/variables';
label {
display: block;
}
}
-.login-form-and-externals {
+.wrapper {
display: flex;
+ justify-content: space-around;
flex-wrap: wrap;
- font-size: 15px;
- form {
- margin: 0 50px 20px 0;
+ & > div {
+ flex: 1 1;
}
- .external-login-blocks {
- min-width: 200px;
+ .login-form-and-externals {
+ display: flex;
+ flex-wrap: wrap;
+ font-size: 15px;
+ max-width: 450px;
+ margin-bottom: 40px;
+ margin-left: 10px;
+ margin-right: 10px;
- .block-title {
- font-weight: $font-semibold;
+ form {
+ 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 {
- @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)
+ .external-login-blocks {
+ min-width: 200px;
+
+ .block-title {
+ font-weight: $font-semibold;
+ }
+
+ .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();
+ }
}
}
import { ActivatedRoute } from '@angular/router'
import { AuthService, Notifier, RedirectService, UserService } from '@app/core'
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 { 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'
@Component({
@ViewChild('usernameInput', { static: false }) usernameInput: ElementRef
@ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef
+ accordion: NgbAccordion
error: string = null
forgotPasswordEmail = ''
externalAuthError = false
externalLogins: string[] = []
+ instanceInformationPanels = {
+ terms: true,
+ administrators: false,
+ features: false,
+ moderation: false,
+ codeOfConduct: false
+ }
+
private openedForgotPasswordModal: NgbModalRef
private serverConfig: ServerConfig
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 () {
return this.serverConfig.email.enabled === false
}
this.openedForgotPasswordModal.close()
}
+ onInstanceAboutAccordionInit (instanceAboutAccordion: InstanceAboutAccordionComponent) {
+ this.accordion = instanceAboutAccordion.accordion
+ }
+
private loadExternalAuthToken (username: string, token: string) {
this.isAuthenticatedWithExternalAuth = true
import { NgModule } from '@angular/core'
import { SharedFormModule } from '@app/shared/shared-forms'
import { SharedGlobalIconModule } from '@app/shared/shared-icons'
+import { SharedInstanceModule } from '@app/shared/shared-instance'
import { SharedMainModule } from '@app/shared/shared-main'
import { LoginRoutingModule } from './login-routing.module'
import { LoginComponent } from './login.component'
SharedMainModule,
SharedFormModule,
- SharedGlobalIconModule
+ SharedGlobalIconModule,
+
+ SharedInstanceModule
],
declarations: [
<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) }" [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)"
>
<div class="step-index">
$grey-color: #9CA3AB;
$index-block-height: 32px;
+.container {
+ padding-left: 0;
+ padding-right: 0;
+ max-width: unset !important;
+}
+
header {
display: flex;
justify-content: space-between;
align-items: center;
width: $index-block-height;
+ &:not(.c-hand) {
+ cursor: default;
+ }
+
.step-index {
display: flex;
justify-content: center;
isCompleted (step: CdkStep) {
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 ]
+ }
}
--- /dev/null
+<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>
--- /dev/null
+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()
+ }
+}
</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>
USER_DISPLAY_NAME_REQUIRED_VALIDATOR,
USER_EMAIL_VALIDATOR,
USER_PASSWORD_VALIDATOR,
- USER_TERMS_VALIDATOR,
USER_USERNAME_VALIDATOR
} from '@app/shared/form-validators/user-validators'
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
styleUrls: [ './register.component.scss' ]
})
export class RegisterStepUserComponent extends FormReactive implements OnInit {
- @Input() hasCodeOfConduct = false
@Input() videoUploadDisabled = false
@Output() formBuilt = new EventEmitter<FormGroup>()
- @Output() termsClick = new EventEmitter<void>()
- @Output() codeOfConductClick = new EventEmitter<void>()
constructor (
protected formValidatorService: FormValidatorService,
displayName: USER_DISPLAY_NAME_REQUIRED_VALIDATOR,
username: USER_USERNAME_VALIDATOR,
password: USER_PASSWORD_VALIDATOR,
- email: USER_EMAIL_VALIDATOR,
- terms: USER_TERMS_VALIDATOR
+ email: USER_EMAIL_VALIDATOR
})
setTimeout(() => this.formBuilt.emit(this.form))
.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) {
const username = this.form.value['username'] || ''
<div class="wrapper" [hidden]="signupDone">
<div class="register-form">
<my-custom-stepper linear *ngIf="!signupDone">
- <cdk-step [stepControl]="formStepUser" i18n-label label="User">
- <my-register-step-user
+ <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"></my-instance-about-accordion>
+ </div>
+
+ <my-register-step-terms
[hasCodeOfConduct]="!!aboutHtml.codeOfConduct"
- [videoUploadDisabled]="videoUploadDisabled"
- (formBuilt)="onUserFormBuilt($event)" (termsClick)="onTermsClick()" (codeOfConductClick)="onCodeOfConductClick()"
- >
- </my-register-step-user>
+ (formBuilt)="onTermsFormBuilt($event)" (termsClick)="onTermsClick()" (codeOfConductClick)="onCodeOfConductClick()"
+ ></my-register-step-terms>
+
+ <button cdkStepperNext [disabled]="!formStepTerms || !formStepTerms.valid">{{ defaultNextStepButtonLabel }}</button>
+ </cdk-step>
+
+ <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 i18n cdkStepperNext [disabled]="!formStepUser || !formStepUser.valid"
- (click)="signup()">{{ videoUploadDisabled ? 'Signup' : 'Next' }}</button>
+ <button cdkStepperNext [disabled]="!formStepUser || !formStepUser.valid" (click)="videoUploadDisabled && signup()">{{ stepUserButtonLabel }}</button>
</cdk-step>
- <cdk-step [stepControl]="formStepChannel" i18n-label label="Channel" *ngIf="!videoUploadDisabled">
+ <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>
<button i18n cdkStepperNext (click)="signup()"
</cdk-step>
</my-custom-stepper>
</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>
@import '_variables';
@import '_mixins';
-@import "./_bootstrap-variables";
-
-@import '~bootstrap/scss/functions';
-@import '~bootstrap/scss/variables';
.alert {
font-size: 15px;
.wrapper {
display: flex;
- justify-content: space-between;
- flex-wrap: wrap;
-
- & > div {
- margin-bottom: 40px;
+ flex-direction: column;
- &.register-form {
- width: 450px;
+ .register-form {
+ max-width: 600px;
+ align-self: center;
+ }
- @media screen and (max-width: $mobile-view) {
- width: 100%;
- }
- }
+ .register-form,
+ .instance-information {
+ width: 100%;
+ }
- &.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;
- }
- }
- }
+ .instance-information {
+ margin-bottom: 15px;
}
}
}
.input-group {
- @include peertube-input-group(400px);
+ @include peertube-input-group(100%);
}
.input-group-append {
height: 30px;
}
+.form-group-terms {
+ width: 100% !important;
+}
+
input:not([type=submit]) {
- @include peertube-input-text(400px);
+ @include peertube-input-text(100%);
display: block;
&#username,
}
}
-@media screen and (max-width: $mobile-view) {
- .form-group-terms,
- .input-group,
- input:not([type=submit]) {
- width: 100%;
- }
-}
-
input[type=submit],
button {
@include peertube-button;
@include orange-button;
-
}
.name-information {
-import { Component, OnInit, ViewChild } from '@angular/core'
+import { Component, OnInit } from '@angular/core'
import { FormGroup } from '@angular/forms'
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 { InstanceService } from '@app/shared/shared-instance'
import { NgbAccordion } from '@ng-bootstrap/ng-bootstrap'
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({
selector: 'my-register',
styleUrls: [ './register.component.scss' ]
})
export class RegisterComponent implements OnInit {
- @ViewChild('accordion', { static: true }) accordion: NgbAccordion
-
+ accordion: NgbAccordion
info: string = null
error: string = null
success: string = null
signupDone = false
- about: About
- aboutHtml = {
- description: '',
- terms: '',
- codeOfConduct: '',
- moderationInformation: '',
- administrator: ''
- }
-
videoUploadDisabled: boolean
+ formStepTerms: FormGroup
formStepUser: 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
constructor (
private route: ActivatedRoute,
private authService: AuthService,
- private notifier: Notifier,
private userService: UserService,
- private instanceService: InstanceService,
private hooks: HooksService
) {
}
this.serverConfig = this.route.snapshot.data.serverConfig
this.videoUploadDisabled = this.serverConfig.user.videoQuota === 0
-
- this.instanceService.getAbout()
- .subscribe(
- async about => {
- this.about = about
-
- this.aboutHtml = await this.instanceService.buildHtml(about)
- },
-
- err => this.notifier.error(err.message)
- )
+ this.stepUserButtonLabel = this.videoUploadDisabled
+ ? $localize`Signup`
+ : this.defaultNextStepButtonLabel
this.hooks.runAction('action:signup.register.init', 'signup')
+
}
hasSameChannelAndAccountNames () {
return this.formStepChannel.value['name']
}
+ onTermsFormBuilt (form: FormGroup) {
+ this.formStepTerms = form
+ }
+
onUserFormBuilt (form: FormGroup) {
this.formStepUser = form
}
if (this.accordion) this.accordion.toggle('code-of-conduct')
}
+ onInstanceAboutAccordionInit (instanceAboutAccordion: InstanceAboutAccordionComponent) {
+ this.accordion = instanceAboutAccordion.accordion
+ this.aboutHtml = instanceAboutAccordion.aboutHtml
+ }
+
async signup () {
this.error = null
import { NgModule } from '@angular/core'
import { SignupSharedModule } from '@app/+signup/shared/signup-shared.module'
import { SharedInstanceModule } from '@app/shared/shared-instance'
-import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
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'
RegisterRoutingModule,
CdkStepperModule,
- NgbAccordionModule,
SignupSharedModule,
RegisterComponent,
CustomStepperComponent,
RegisterStepChannelComponent,
+ RegisterStepTermsComponent,
RegisterStepUserComponent
],
}
export const USER_TERMS_VALIDATOR: BuildFormValidator = {
- VALIDATORS: [
- Validators.requiredTrue
- ],
+ VALIDATORS: [ Validators.requiredTrue ],
MESSAGES: {
'required': $localize`You must agree with the instance terms in order to register on it.`
}
export * from './feature-boolean.component'
+export * from './instance-about-accordion.component'
export * from './instance-features-table.component'
export * from './instance-follow.service'
export * from './instance-statistics.component'
--- /dev/null
+<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>
--- /dev/null
+@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%;
+ }
+}
--- /dev/null
+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
+ }
+}
import { NgModule } from '@angular/core'
+import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
import { SharedMainModule } from '../shared-main/shared-main.module'
import { FeatureBooleanComponent } from './feature-boolean.component'
+import { InstanceAboutAccordionComponent } from './instance-about-accordion.component'
import { InstanceFeaturesTableComponent } from './instance-features-table.component'
import { InstanceFollowService } from './instance-follow.service'
import { InstanceStatisticsComponent } from './instance-statistics.component'
@NgModule({
imports: [
- SharedMainModule
+ SharedMainModule,
+ NgbAccordionModule
],
declarations: [
FeatureBooleanComponent,
+ InstanceAboutAccordionComponent,
InstanceFeaturesTableComponent,
InstanceStatisticsComponent
],
exports: [
FeatureBooleanComponent,
+ InstanceAboutAccordionComponent,
InstanceFeaturesTableComponent,
InstanceStatisticsComponent
],