diff options
Diffstat (limited to 'Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts')
-rw-r--r-- | Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts | 124 |
1 files changed, 87 insertions, 37 deletions
diff --git a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts index 4767bd16..6de355f2 100644 --- a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts +++ b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts @@ -1,11 +1,9 @@ -import { Injectable } from '@angular/core'; +import { Injectable, Inject } from '@angular/core'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { Router } from '@angular/router'; -import { Observable, of, throwError, BehaviorSubject } from 'rxjs'; -import { map, catchError, retry } from 'rxjs/operators'; - -import { nullIfUndefined } from '../../utilities/language-untilities'; +import { Observable, throwError, BehaviorSubject, of } from 'rxjs'; +import { map, catchError, retry, switchMap, tap, filter } from 'rxjs/operators'; import { AlreadyLoginError, BadCredentialsError, BadNetworkError, UnknownError } from './errors'; import { @@ -13,9 +11,24 @@ import { CreateTokenResponse, ValidateTokenRequest, ValidateTokenResponse } from './http-entities'; import { UserCredentials, UserInfo } from '../entities'; +import { MatSnackBar } from '@angular/material'; +import { WINDOW } from '../window-inject-token'; + +export const snackBarText = { + checkFail: 'Failed to check last login', + noLogin: 'No login before!', + alreadyLogin: 'You have login already!', + invalidLogin: 'Last login is no longer invalid!', + ok: 'ok' +}; +export type SnackBarTextKey = Exclude<keyof typeof snackBarText, 'ok'>; -export type UserLoginState = 'nologin' | 'invalidlogin' | 'success'; +export const TOKEN_STORAGE_KEY = 'token'; + +export interface LoginInfo extends UserCredentials { + rememberMe: boolean; +} /** * This service is only used internal in user module. @@ -26,56 +39,80 @@ export type UserLoginState = 'nologin' | 'invalidlogin' | 'success'; export class InternalUserService { private token: string | null = null; - private userInfoSubject = new BehaviorSubject<UserInfo | null>(null); + private userInfoSubject = new BehaviorSubject<UserInfo | null | undefined>(undefined); + + readonly userInfo$: Observable<UserInfo | null> = + <Observable<UserInfo | null>>this.userInfoSubject.pipe(filter(value => value !== undefined)); - get currentUserInfo(): UserInfo | null { + get currentUserInfo(): UserInfo | null | undefined { return this.userInfoSubject.value; } - get userInfo$(): Observable<UserInfo | null> { - return this.userInfoSubject; + private openSnackBar(snackBar: MatSnackBar, textKey: SnackBarTextKey) { + setTimeout(() => snackBar.open(snackBarText[textKey], snackBarText.ok, { duration: 2000 }), 0); } - constructor(private httpClient: HttpClient, private router: Router) { } - - userRouteNavigate(commands: any[] | null) { - this.router.navigate([{ - outlets: { - user: commands - } - }]); - } - - refreshAndGetUserState(): Observable<UserLoginState> { - if (this.token === undefined || this.token === null) { - return of(<UserLoginState>'nologin'); + constructor(@Inject(WINDOW) private window: Window, private httpClient: HttpClient, private router: Router, snackBar: MatSnackBar) { + const savedToken = this.window.localStorage.getItem(TOKEN_STORAGE_KEY); + if (savedToken === null) { + this.openSnackBar(snackBar, 'noLogin'); + this.userInfoSubject.next(null); + } else { + this.validateToken(savedToken).subscribe(result => { + if (result === null) { + this.window.localStorage.removeItem(TOKEN_STORAGE_KEY); + this.openSnackBar(snackBar, 'invalidLogin'); + this.userInfoSubject.next(null); + } else { + this.token = savedToken; + this.userInfoSubject.next(result); + this.openSnackBar(snackBar, 'alreadyLogin'); + } + }, _ => { + this.openSnackBar(snackBar, 'checkFail'); + this.userInfoSubject.next(null); + }); } + } - return this.httpClient.post<ValidateTokenResponse>(validateTokenUrl, <ValidateTokenRequest>{ token: this.token }).pipe( + private validateToken(token: string): Observable<UserInfo | null> { + return this.httpClient.post<ValidateTokenResponse>(validateTokenUrl, <ValidateTokenRequest>{ token: token }).pipe( retry(3), - catchError(error => { - console.error('Failed to validate token.'); - return throwError(error); - }), - map(result => { + switchMap(result => { if (result.isValid) { - this.userInfoSubject.next(nullIfUndefined(result.userInfo)); - return <UserLoginState>'success'; + const { userInfo } = result; + if (userInfo) { + return of(userInfo); + } else { + return throwError(new Error('Wrong server response. IsValid is true but UserInfo is null.')); + } } else { - this.token = null; - this.userInfoSubject.next(null); - return <UserLoginState>'invalidlogin'; + return of(null); } - }) + }), + tap({ + error: error => { + console.error('Failed to validate token.'); + console.error(error); + } + }), ); } - tryLogin(credentials: UserCredentials): Observable<UserInfo> { + userRouteNavigate(commands: any[] | null) { + this.router.navigate([{ + outlets: { + user: commands + } + }]); + } + + tryLogin(info: LoginInfo): Observable<UserInfo> { if (this.token) { return throwError(new AlreadyLoginError()); } - return this.httpClient.post<CreateTokenResponse>(createTokenUrl, <CreateTokenRequest>credentials).pipe( + return this.httpClient.post<CreateTokenResponse>(createTokenUrl, <CreateTokenRequest>info).pipe( catchError((error: HttpErrorResponse) => { if (error.error instanceof ErrorEvent) { console.error('An error occurred when login: ' + error.error.message); @@ -90,9 +127,22 @@ export class InternalUserService { }), map(result => { this.token = result.token; + if (info.rememberMe) { + this.window.localStorage.setItem(TOKEN_STORAGE_KEY, result.token); + } this.userInfoSubject.next(result.userInfo); return result.userInfo; }) ); } + + logout() { + if (this.currentUserInfo === null) { + throw new Error('No login now. You can\'t logout.'); + } + + this.window.localStorage.removeItem(TOKEN_STORAGE_KEY); + this.token = null; + this.userInfoSubject.next(null); + } } |