import { Injectable } from "@angular/core"; import { Router } from "@angular/router"; import { Actions, Effect, ofType } from "@ngrx/effects"; import { select, Store } from "@ngrx/store"; import { EMPTY, of, Subscription } from "rxjs"; import { map, catchError, exhaustMap, tap, switchMap, withLatestFrom } from "rxjs/operators"; import { User } from "../../models/user.model"; import { AuthService } from "../../services/auth.service"; import { AuthActionsTypes, SetCurrentUser, SigninError, SigninSuccess, SignupError, SignupSuccess, TryFetchCurrentUser, TrySignin, TrySignup } from "../actions/auth.actions"; import { State } from "../../store"; import { tokenAuthSelector } from "../selectors/auth.selectors"; import { UserService } from "../../services/user.service"; import { HttpErrorResponse } from "@angular/common/http"; @Injectable() export class AuthEffects { private subscription: Subscription = null; constructor( private actions$: Actions, private authService: AuthService, private router: Router, private store: Store<State>, private userService: UserService ) {} @Effect() trySignUp$ = this.actions$.pipe( ofType(AuthActionsTypes.TRY_SIGNUP), map(( action: TrySignup ) => action.payload), exhaustMap((user: User) => this.authService .signup(user) .pipe( map((user: User) => new SignupSuccess(user)), catchError((error: any) => of(new SignupError(error))) ) ) ); @Effect({dispatch: false}) signupSuccess$ = this.actions$.pipe( ofType(AuthActionsTypes.SIGNUP_SUCCESS), tap(() => this.router.navigate(['/signin'], {queryParams: { registered: 'true' } })) ); @Effect() trySignin$ = this.actions$.pipe( ofType(AuthActionsTypes.TRY_SIGNIN), map(( action: TrySignin ) => action.payload), exhaustMap((credentials: { email: string, password: string}) => this.authService.signin(credentials).pipe( map((token: string) => { return new SigninSuccess(token) }), catchError((error: HttpErrorResponse) => { let message = `Erreur serveur survenue. Réessayer ultérieurement.`; if (error.status === 401) { message = `Le mot de passe ou l'adresse mail n'existent pas.`; } if (error.status === 404) { message = `Base de données indisponible pour le moment. Réessayer ultérieurement.`; } return of(new SigninError(message)) }) ) ) ); // On active la subscription pour le initTimer uniquement s'il n'existe pas déjà un subscription // On veut un seul initTimer(). On l'init quand l'user se connecte @Effect({ dispatch: false }) signinSuccess$ = this.actions$.pipe( ofType(AuthActionsTypes.SIGNIN_SUCCESS), map((action: SigninSuccess) => action.payload), tap((token: string) => { localStorage.setItem('jwt', token); if (this.subscription === null) { this.subscription = this.authService.initRefreshToken().subscribe(); this.router.navigate(['/profile']); } }), ); // Si on a pas de token, on ne va pas demandé un refresh_token au back_end. // On utilise donc le store dans l'effet pour le vérifier (withLatestFrom) @Effect() tryRefreshToken$ = this.actions$.pipe( ofType(AuthActionsTypes.TRY_REFRESH_TOKEN), withLatestFrom(this.store.pipe(select(tokenAuthSelector))), switchMap(([action, token]) => { if (token) { return this.authService.refreshToken().pipe( map((jwtoken: string) => new SigninSuccess(jwtoken)), catchError((err: any) => { if(this.subscription) { this.subscription.unsubscribe(); this.subscription = null; } localStorage.removeItem('jwt'); return EMPTY; }) ); } else { return EMPTY; } }) ); @Effect({ dispatch: false }) logout$ = this.actions$.pipe( ofType(AuthActionsTypes.LOGOUT), tap((err: any) => { if(this.subscription) { this.subscription.unsubscribe(); } localStorage.removeItem('jwt'); this.router.navigate(['/']); }) ) @Effect() tryFetchCurrentUser$ = this.actions$.pipe( ofType(AuthActionsTypes.TRY_FETCH_CURRENT_USER), switchMap(() => { return this.userService.getCurrentUser().pipe( map((user: User) => new SetCurrentUser(user)), catchError((err: any) => { console.warn('error in tryFetchCurrentUser') return EMPTY; }) ); }) ); }