import { Navigate } from '@ngxs/router-plugin' ; import { Action , NgxsOnInit , Selector , State , StateContext } from '@ngxs/store' ; import { throwError } from 'rxjs' ; import { catchError , finalize , switchMap , tap , map } from 'rxjs/operators' ; import { Logout } from '../../auth/stores/auth.state' ; import { errorToMessage } from '../../shared/util/api-error' ; import { Project } from '../models/project' ; import { ProjectsService } from '../services/projects.service' ; import { ProjectStats } from '../models/project-stats' ; import { ProjectStatsService } from '../services/project-stats.service' ; import { Injectable } from '@angular/core' ; export class ClearMessages { static readonly type = '[Projects] ClearMessages' ; } export class GetProjects { static readonly type = '[Projects] Get projects' ; } export class ClearCurrentProject { static readonly type = '[Projects] Clear current project' ; } export class SetCurrentProject { static readonly type = '[Projects] Set current project' ; constructor ( public id ) { } } export class RefreshProjectStats { static readonly type = '[Projects] Refresh current project stats' ; constructor ( ) { } } export class UpdateProject { static readonly type = '[Projects] Update project' ; constructor ( public id , public data ) { } } export class DeleteProject { static readonly type = '[Projects] Delete project' ; constructor ( public id ) { } } export class CreateProject { static readonly type = '[Projects] Create project' ; constructor ( public name , public description ) { } } export class ReloadCurrentProject { static readonly type = '[Projects] Reload current project' ; } export interface ProjectsStateModel { projects : Project [ ] ; currentProject : Project | undefined ; currentProjectStats : ProjectStats | undefined ; isLoading : boolean ; errorMessage : string | undefined ; } const stateDefaults = { projects : [ ] , currentProject : undefined , currentProjectStats : undefined , isLoading : false , errorMessage : undefined , } ; @ State < ProjectsStateModel > ( { name : 'projects' , defaults : stateDefaults , } ) @ Injectable ( { providedIn : 'root' } ) export class ProjectsState implements NgxsOnInit { constructor ( private projectsService , private projectStatsService ) { } @ Selector ( ) static projects ( state ) { return state . projects ; } @ Selector ( ) static isLoading ( state ) { return state . isLoading ; } @ Selector ( ) static currentProject ( state ) { return state . currentProject ; } @ Selector ( ) static currentProjectStats ( state ) { return state . currentProjectStats ; } ngxsOnInit ( ctx ) { } @ Action ( Logout ) logout ( ctx , action ) { ctx . setState ( stateDefaults ) ; } @ Action ( GetProjects ) getProjects ( ctx ) { ctx . patchState ( { isLoading : true } ) ; return this . projectsService . find ( ) . pipe ( tap ( projects => ctx . patchState ( { projects } ) ) , catchError ( error => { ctx . patchState ( { errorMessage : errorToMessage ( error ) } ) ; return throwError ( error ) ; } ) , finalize ( ( ) => ctx . patchState ( { isLoading : false } ) ) ) ; } @ Action ( CreateProject ) createProject ( ctx , action ) { ctx . patchState ( { isLoading : true } ) ; return this . projectsService . create ( action ) . pipe ( tap ( project => { ctx . patchState ( { projects : [ project , ... ctx . getState ( ) . projects ] } ) ; } ) , catchError ( error => { ctx . patchState ( { errorMessage : errorToMessage ( error ) } ) ; return throwError ( error ) ; } ) , finalize ( ( ) => ctx . patchState ( { isLoading : false } ) ) ) ; } @ Action ( UpdateProject ) updateProject ( ctx , action ) { ctx . patchState ( { isLoading : true } ) ; return this . projectsService . update ( action . id , action . data ) . pipe ( tap ( project => { const currentProject = ctx . getState ( ) . currentProject ; if ( project . id === currentProject . id ) { ctx . patchState ( { currentProject : { ... currentProject , ... project } } ) ; } } ) , catchError ( error => { ctx . patchState ( { errorMessage : errorToMessage ( error ) } ) ; return throwError ( error ) ; } ) , finalize ( ( ) => ctx . patchState ( { isLoading : false } ) ) ) ; } @ Action ( DeleteProject ) deleteProject ( ctx , action ) { ctx . patchState ( { isLoading : true } ) ; return this . projectsService . delete ( action . id ) . pipe ( tap ( ( ) => { ctx . dispatch ( new Navigate ( [ '/projects' ] ) ) ; } ) , catchError ( error => { ctx . patchState ( { errorMessage : errorToMessage ( error ) } ) ; return throwError ( error ) ; } ) , finalize ( ( ) => ctx . patchState ( { isLoading : false } ) ) ) ; } @ Action ( SetCurrentProject ) setCurrentProject ( ctx , action ) { ctx . patchState ( { isLoading : true , currentProject : undefined } ) ; return this . projectsService . findOne ( action . id ) . pipe ( switchMap ( project => this . projectsService . findPlan ( action . id ) . pipe ( tap ( plan => { project . plan = plan ; ctx . patchState ( { currentProject : project } ) ; } ) ) ) , tap ( ( ) => ctx . dispatch ( new RefreshProjectStats ( ) ) ) , catchError ( error => { if ( error . status === 404 ) { ctx . dispatch ( new Navigate ( [ '404' ] ) ) ; } ctx . patchState ( { errorMessage : errorToMessage ( error ) } ) ; return throwError ( error ) ; } ) , finalize ( ( ) => ctx . patchState ( { isLoading : false } ) ) ) ; } @ Action ( ReloadCurrentProject ) reloadCurrentProject ( ctx ) { ctx . patchState ( { isLoading : true } ) ; const projectId = ctx . getState ( ) . currentProject . id ; return this . projectsService . findOne ( projectId ) . pipe ( switchMap ( project => this . projectsService . findPlan ( projectId ) . pipe ( tap ( plan => { project . plan = plan ; ctx . patchState ( { currentProject : project } ) ; } ) ) ) , catchError ( error => { if ( error . status === 404 ) { ctx . dispatch ( new Navigate ( [ '404' ] ) ) ; } ctx . patchState ( { errorMessage : errorToMessage ( error ) } ) ; return throwError ( error ) ; } ) , finalize ( ( ) => ctx . patchState ( { isLoading : false } ) ) ) ; } @ Action ( RefreshProjectStats ) refreshProjectStats ( ctx ) { ctx . patchState ( { isLoading : true } ) ; const projectId = ctx . getState ( ) . currentProject . id ; return this . projectStatsService . getStats ( projectId ) . pipe ( map ( stats => ctx . patchState ( { currentProjectStats : stats } ) ) , catchError ( error => { ctx . patchState ( { errorMessage : errorToMessage ( error ) } ) ; return throwError ( error ) ; } ) , finalize ( ( ) => ctx . patchState ( { isLoading : false } ) ) ) ; } @ Action ( ClearCurrentProject ) clearCurrentProject ( ctx ) { ctx . patchState ( { currentProject : undefined , errorMessage : undefined } ) ; } @ Action ( ClearMessages ) clearMessages ( ctx ) { ctx . patchState ( { errorMessage : undefined } ) ; } }