Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements login and sign-up pages UX/UI #3357

Merged
merged 25 commits into from
Dec 7, 2020
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4efa603
New login form ui
kimsible Nov 26, 2020
f95aa8a
Move InstanceAboutAccordion to shared components
kimsible Nov 27, 2020
c0917e7
Update closed registration alert text
kimsible Nov 28, 2020
5ed7c67
Add alert for opened registration and move them bellow login form
kimsible Nov 28, 2020
70e7bd3
Adjust flex block on signup and login views
kimsible Nov 28, 2020
ffba67c
Replace toggle accordion with expand on links in signup and login + s…
kimsible Nov 29, 2020
ee41e99
Improve display of login alerts
kimsible Nov 29, 2020
d51aa09
Fix missing Component suffix
kimsible Nov 29, 2020
4aa48f2
Define min-width instance-information block sign-up and login for mob…
kimsible Nov 29, 2020
1762d62
Add ability to select specific panels in instanceAboutAccorddion
kimsible Nov 30, 2020
1bc5342
Add instance title and short-description to common instanceAboutAccor…
kimsible Nov 30, 2020
9b94ac2
Reverse blocks for smaller 768px width screens in login and sign-up v…
kimsible Nov 30, 2020
52509d7
Clarify title alert in login page
kimsible Nov 30, 2020
6cedf93
Adjust instance about accordion max-width on small screens
kimsible Nov 30, 2020
732e954
Fix missing parameters in instance accordion filters panels
kimsible Nov 30, 2020
10e1aa3
Improve instance about accordion UI
kimsible Nov 30, 2020
d8ff33f
Adjust reversed block in signup page for screens smaller than 1200px
kimsible Nov 30, 2020
6d3bf4b
Adjust form-group input width in sign-up page
kimsible Nov 30, 2020
ca43ce2
Limit short-description to 3 lines in sign-up and login pages
kimsible Dec 2, 2020
4ddaee0
Add step terms for signup
kimsible Dec 2, 2020
b743971
Reverse Code-of-Conduct and Terms order panels in sign-up terms step
kimsible Dec 2, 2020
dddc146
Adjust sign-up page width on mobile screens
kimsible Dec 2, 2020
a2bcf7c
rebase and adjust PR in registration form
rigelk Dec 7, 2020
184ea64
adopt simpler isAccessible check for stepper
rigelk Dec 7, 2020
4f92288
normalize tags and condition checks in templates
rigelk Dec 7, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 58 additions & 50 deletions client/src/app/+login/login.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,73 +8,81 @@
</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>

<div *ngIf="formErrors.username" class="form-error">
{{ formErrors.username }}
<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>

<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>
rigelk marked this conversation as resolved.
Show resolved Hide resolved
<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 i18n-title class="forgot-password-button" (click)="openForgotPasswordModal()" title="Click here to reset your password">I forgot my password</a>
rigelk marked this conversation as resolved.
Show resolved Hide resolved
<div *ngIf="signupAllowed" class="signup-link">
<span>·</span>
rigelk marked this conversation as resolved.
Show resolved Hide resolved
<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 === true" i18n>
rigelk marked this conversation as resolved.
Show resolved Hide resolved
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 === false" i18n>
rigelk marked this conversation as resolved.
Show resolved Hide resolved
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>

Expand Down
150 changes: 126 additions & 24 deletions client/src/app/+login/login.component.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
@import '_variables';
@import '_mixins';
@import './_bootstrap-variables';
@import '~bootstrap/scss/functions';
@import '~bootstrap/scss/variables';

label {
display: block;
Expand Down Expand Up @@ -57,39 +60,138 @@ input[type=submit] {
}
}

.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();
}
}
}
25 changes: 24 additions & 1 deletion client/src/app/+login/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angula
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({
Expand All @@ -18,13 +19,22 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
@ViewChild('usernameInput', { static: false }) usernameInput: ElementRef
@ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef

accordion: NgbAccordion
error: string = null
forgotPasswordEmail = ''

isAuthenticatedWithExternalAuth = false
externalAuthError = false
externalLogins: string[] = []

instanceInformationPanels = {
terms: true,
administrators: false,
features: false,
moderation: false,
codeOfConduct: false
}

private openedForgotPasswordModal: NgbModalRef
private serverConfig: ServerConfig

Expand All @@ -45,6 +55,15 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
return this.serverConfig.signup.allowed === true
}

onTermsClick (event: Event, instanceInformation: HTMLElement) {
event.preventDefault()

if (this.accordion) {
rigelk marked this conversation as resolved.
Show resolved Hide resolved
this.accordion.expand('terms')
instanceInformation.scrollIntoView({ behavior: 'smooth' })
}
}

isEmailDisabled () {
return this.serverConfig.email.enabled === false
}
Expand Down Expand Up @@ -122,6 +141,10 @@ The link will expire within 1 hour.`
this.openedForgotPasswordModal.close()
}

onInstanceAboutAccordionInit (instanceAboutAccordion: InstanceAboutAccordionComponent) {
this.accordion = instanceAboutAccordion.accordion
}

private loadExternalAuthToken (username: string, token: string) {
this.isAuthenticatedWithExternalAuth = true

Expand Down
5 changes: 4 additions & 1 deletion client/src/app/+login/login.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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'
Expand All @@ -11,7 +12,9 @@ import { LoginComponent } from './login.component'

SharedMainModule,
SharedFormModule,
SharedGlobalIconModule
SharedGlobalIconModule,

SharedInstanceModule
],

declarations: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<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">
Expand Down
Loading