aboutsummaryrefslogtreecommitdiff
path: root/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts
diff options
context:
space:
mode:
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.ts124
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);
+ }
}