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

NAS-130956 / 25.04 / Reduce numbers of layout shifts on login page #10612

Merged
merged 16 commits into from
Sep 16, 2024
Merged
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
:host {
align-items: center;
display: flex;
padding: 32px 32px 0;
padding: 28px 16px 10px;
}

.fake-icon {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
<p class="failover-status-message">{{ statusMessage() | translate }}</p>
@if (!disabledReasons()) {
<ngx-skeleton-loader></ngx-skeleton-loader>
}
@if (areReasonsShown()) {
<div class="failover-reasons">
@for (reason of disabledReasons(); track reason) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
:host {
display: block;
margin-top: 10px;
text-align: center;
}

p {
margin-bottom: 0.4rem;
margin-top: 0.4rem;
margin: 0.4rem;
}

ngx-skeleton-loader {
display: inline-block;
flex: 0;
height: 20px;
width: 80%;

&::ng-deep {
span.loader {
height: 22px;
margin: 0;
}
}
}
6 changes: 3 additions & 3 deletions src/app/pages/signin/signin-form/signin-form.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
type="button"
color="primary"
ixTest="log-in"
[disabled]="isLoading$ | async"
[disabled]="isFormDisabled()"
(click)="login()"
>
{{ 'Log In' | translate }}
Expand Down Expand Up @@ -58,7 +58,7 @@
type="button"
color="primary"
ixTest="log-in"
[disabled]="isLoading$ | async"
[disabled]="isFormDisabled()"
(click)="loginWithOtp()"
>
{{ 'Proceed' | translate }}
Expand All @@ -68,7 +68,7 @@
mat-button
type="button"
ixTest="otp-log-in"
[disabled]="isLoading$ | async"
[disabled]="isFormDisabled()"
(click)="cancelOtpLogin()"
>{{ 'Cancel' | translate }}</button>
</div>
Expand Down
7 changes: 7 additions & 0 deletions src/app/pages/signin/signin-form/signin-form.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@
display: inline-block;
}
}

input,
button {
&:disabled {
cursor: not-allowed;
}
}
18 changes: 15 additions & 3 deletions src/app/pages/signin/signin-form/signin-form.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { AsyncPipe } from '@angular/common';
import {
ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnInit,
ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, effect, Inject, input, OnInit,
} from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import {
FormBuilder, Validators, FormsModule, ReactiveFormsModule,
} from '@angular/forms';
Expand Down Expand Up @@ -44,6 +45,8 @@ import { WebSocketService } from 'app/services/ws.service';
],
})
export class SigninFormComponent implements OnInit {
disabled = input.required<boolean>();

hasTwoFactor = false;
showSecurityWarning = false;

Expand All @@ -57,7 +60,8 @@ export class SigninFormComponent implements OnInit {
otp: ['', Validators.required],
});

protected isLoading$ = this.signinStore.isLoading$;
protected isLoading = toSignal(this.signinStore.isLoading$);
readonly isFormDisabled = computed(() => this.disabled() || this.isLoading());

constructor(
private formBuilder: FormBuilder,
Expand All @@ -69,11 +73,19 @@ export class SigninFormComponent implements OnInit {
private cdr: ChangeDetectorRef,
@Inject(WINDOW) private window: Window,
) {
effect(() => {
if (this.isFormDisabled()) {
this.form.disable();
} else {
this.form.enable();
}
});

if (this.window.location.protocol !== 'https:') {
this.showSecurityWarning = true;
}

this.isLoading$.pipe(
this.signinStore.isLoading$.pipe(
filter((isLoading) => !isLoading),
take(1),
untilDestroyed(this),
Expand Down
105 changes: 62 additions & 43 deletions src/app/pages/signin/signin.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,74 @@

<div class="page-wrap login-form">
<div class="session-form-hold">
<mat-progress-bar
value="0"
class="session-progress"
[mode]="(hasLoadingIndicator$ | async) ? 'indeterminate' : 'determinate'"
></mat-progress-bar>
<mat-card>
<mat-card-content>
@if (isConnected$ | async) {
<div>
<div class="logo-wrapper">
<ix-icon name="ix:logo_truenas_scale_full" class="logo"></ix-icon>
</div>
@if ((isConnected$ | async) || (!(isConnected$ | async) && (isConnectedDelayed$ | async) !== null)) {
<mat-progress-bar
value="0"
class="session-progress"
[mode]="(hasLoadingIndicator$ | async) ? 'indeterminate' : 'determinate'"
></mat-progress-bar>
}

<div class="card-bottom">
@if (canLogin$ | async) {
<div class="form-container">
@if (wasAdminSet$ | async) {
<ix-signin-form></ix-signin-form>
} @else {
<ix-set-admin-password-form></ix-set-admin-password-form>
}
@if (!(isConnected$ | async) && (isConnectedDelayed$ | async) !== null) {
<mat-card>
<mat-card-content>
<ix-disconnected-message
[hasFailover]="hasFailover$ | async"
></ix-disconnected-message>
</mat-card-content>
</mat-card>
} @else if (isConnected$ | async) {
@if (hasAuthToken && (isTokenWithinTimeline$ | async)) {
<mat-card>
<mat-card-content>
<h3 class="logging-in">{{ 'Logging in...' | translate }}</h3>
</mat-card-content>
</mat-card>
} @else {
<mat-card>
<mat-card-content>
<div>
<div class="logo-wrapper">
<ix-icon name="ix:logo_truenas_scale_full" class="logo"></ix-icon>
</div>
}

@if (failover$ | async; as failover) {
@if (hasFailover$ | async) {
<ix-failover-status
class="failover-status"
[status]="failover.status"
[failoverIps]="failover.ips"
[disabledReasons]="failover.disabledReasons"
></ix-failover-status>
}
}
<div class="card-bottom">
<div class="form-container">
@if (wasAdminSet$ | async) {
<ix-signin-form [disabled]="!(canLogin$ | async)"></ix-signin-form>
} @else {
<ix-set-admin-password-form></ix-set-admin-password-form>
}
</div>

@if (failover$ | async; as failover) {
@if (hasFailover$ | async) {
<ix-failover-status
class="failover-status"
[status]="failover.status"
[failoverIps]="failover.ips"
[disabledReasons]="failover.disabledReasons"
></ix-failover-status>
}
}

<ix-true-command-status></ix-true-command-status>
<ix-true-command-status></ix-true-command-status>

<div class="ix-logo">
<img src="assets/images/ix_logo_full.png" height="40" width="80" alt="iX Systems">
<div class="ix-logo">
<img src="assets/images/ix_logo_full.png" height="40" width="80" alt="iX Systems">
</div>
<ix-copyright-line class="copyright" [withIxLogo]="false"></ix-copyright-line>
</div>
</div>
<ix-copyright-line class="copyright" [withIxLogo]="false"></ix-copyright-line>
</div>
</div>
} @else {
<ix-disconnected-message
[hasFailover]="hasFailover$ | async"
></ix-disconnected-message>
</mat-card-content>
</mat-card>
}
</mat-card-content>
</mat-card>
} @else {
<div class="logo-with-animation-wrapper">
<div>
<ix-icon name="ix:logo_truenas_scale_full"></ix-icon>
</div>
</div>
}
</div>
</div>
34 changes: 30 additions & 4 deletions src/app/pages/signin/signin.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
}

.card-bottom {
overflow: auto;
padding: 0 16px;
}

Expand All @@ -58,19 +59,20 @@
}

.form-container {
margin-bottom: 20px;
padding: 20px 32px 8px;
}

.logo-wrapper {
background-color: var(--bg2);

// Fallback for browsers that don't support image-set
background-image: url('assets/images/signin/stars-sky-800w.jpg');
background-image: image-set(url('assets/images/signin/stars-sky-400w.avif') 1x,
url('assets/images/signin/stars-sky-800w.avif') 2x,
url('assets/images/signin/stars-sky-1200w.avif') 3x);
background-size: cover;
border-bottom: solid 1px var(--lines);
height: 250px;

padding: 20% 0;
position: relative;
Expand All @@ -85,6 +87,24 @@
}
}

.logo-with-animation-wrapper {
align-items: center;
display: flex;
height: 100vh;
justify-content: center;

> div {
animation: loading 2s linear infinite;
height: 65px;
opacity: 0.3;
width: 300px;

ix-icon {
fill: var(--fg1);
}
}
}

@media (max-width: 600px) {
.page-wrap {
bottom: 0;
Expand Down Expand Up @@ -122,11 +142,17 @@
}

.logo-wrapper {
align-items: center;
display: flex;
justify-content: center;
padding: 20% 0;
}
}

.failover-status {
margin-top: 25px;
.logging-in {
box-sizing: border-box;
color: var(--fg2);
padding: 49px 16px 37px;
text-align: center;
width: 100%;
}

28 changes: 25 additions & 3 deletions src/app/pages/signin/signin.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import { MatCard, MatCardContent } from '@angular/material/card';
import { MatFormField } from '@angular/material/form-field';
import { MatInput } from '@angular/material/input';
import { MatProgressBar } from '@angular/material/progress-bar';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { combineLatest } from 'rxjs';
import { TranslateModule } from '@ngx-translate/core';
import { combineLatest, Observable, of } from 'rxjs';
import {
delay,
filter, map, switchMap, take,
} from 'rxjs/operators';
import { WINDOW } from 'app/helpers/window.helper';
Expand All @@ -23,6 +26,8 @@ import { SetAdminPasswordFormComponent } from 'app/pages/signin/set-admin-passwo
import { SigninFormComponent } from 'app/pages/signin/signin-form/signin-form.component';
import { SigninStore } from 'app/pages/signin/store/signin.store';
import { TrueCommandStatusComponent } from 'app/pages/signin/true-command-status/true-command-status.component';
import { AuthService } from 'app/services/auth/auth.service';
import { TokenLastUsedService } from 'app/services/token-last-used.service';
import { WebSocketConnectionService } from 'app/services/websocket-connection.service';

@UntilDestroy()
Expand All @@ -35,6 +40,7 @@ import { WebSocketConnectionService } from 'app/services/websocket-connection.se
imports: [
MatFormField,
MatInput,
MatProgressSpinner,
TestIdModule,
MatProgressBar,
MatCard,
Expand All @@ -46,24 +52,40 @@ import { WebSocketConnectionService } from 'app/services/websocket-connection.se
TrueCommandStatusComponent,
DisconnectedMessageComponent,
AsyncPipe,
TranslateModule,
CopyrightLineComponent,
],
providers: [SigninStore],
})
export class SigninComponent implements OnInit {
protected hasAuthToken = this.authService.hasAuthToken;
protected isTokenWithinTimeline$ = this.tokenLastUsedService.isTokenWithinTimeline$;

readonly wasAdminSet$ = this.signinStore.wasAdminSet$;
readonly failover$ = this.signinStore.failover$;
readonly hasFailover$ = this.signinStore.hasFailover$;
readonly canLogin$ = this.signinStore.canLogin$;
readonly isConnected$ = this.wsManager.isConnected$;
readonly hasLoadingIndicator$ = combineLatest([this.signinStore.isLoading$, this.isConnected$]).pipe(
map(([isLoading, isConnected]) => isLoading || !isConnected),
readonly isConnectedDelayed$: Observable<boolean> = of(null).pipe(
delay(1000),
switchMap(() => this.isConnected$),
);
readonly hasLoadingIndicator$ = combineLatest([
this.signinStore.isLoading$,
this.isConnected$,
this.isTokenWithinTimeline$,
]).pipe(
map(([isLoading, isConnected, isTokenWithinTimeline]) => {
return isLoading || !isConnected || (isTokenWithinTimeline && this.hasAuthToken);
}),
);

constructor(
private wsManager: WebSocketConnectionService,
private signinStore: SigninStore,
private dialog: DialogService,
private authService: AuthService,
private tokenLastUsedService: TokenLastUsedService,
@Inject(WINDOW) private window: Window,
) {}

Expand Down
Loading
Loading