import { Injectable } from '@angular/core' ; import { FormControl } from '@angular/forms' ; import { BehaviorSubject , Observable , combineLatest , of } from 'rxjs' ; import { map , distinctUntilChanged , switchMap , tap , catchError , } from 'rxjs/operators' ; import { MindsHttpClient } from '../../../common/api/client.service' ; import fakeData from './fake-data' ; import { Response , UserState } from '../../../interfaces/dashboard' ; let _state = fakeData [ 0 ] ; const deepDiff = ( prev , curr ) => JSON . stringify ( prev ) === JSON . stringify ( curr ) ; @ Injectable ( ) export class AnalyticsDashboardService { private store = new BehaviorSubject < UserState > ( _state ) ; private state$ = this . store . asObservable ( ) ; category$ = this . state$ . pipe ( map ( state => state . category ) , distinctUntilChanged ( deepDiff ) ) ; description$ = this . state$ . pipe ( map ( state => state . description ) , distinctUntilChanged ( ) ) ; timespan$ = this . state$ . pipe ( map ( state => state . timespan ) , distinctUntilChanged ( deepDiff ) ) ; timespans$ = this . state$ . pipe ( map ( state => state . timespans ) , distinctUntilChanged ( deepDiff ) ) ; metric$ = this . state$ . pipe ( map ( state => state . metric ) , distinctUntilChanged ( deepDiff ) ) ; metrics$ = this . state$ . pipe ( map ( state => state . metrics ) , distinctUntilChanged ( deepDiff ) ) ; filter$ = this . state$ . pipe ( map ( state => state . filter ) , distinctUntilChanged ( deepDiff ) ) ; filters$ = this . state$ . pipe ( map ( state => state . filters ) , distinctUntilChanged ( deepDiff ) ) ; loading$ = new BehaviorSubject < boolean > ( false ) ; ready$ = new BehaviorSubject < boolean > ( false ) ; /**
     * Viewmodel that resolves once all the data is ready (or updated)
     */ vm$ : Observable < UserState > = new BehaviorSubject ( _state ) ; constructor ( private http ) { this . loadFromRemote ( ) ; } loadFromRemote ( ) { combineLatest ( [ this . category$ , this . timespan$ , this . metric$ , this . filter$ ] ) . pipe ( distinctUntilChanged ( deepDiff ) , catchError ( _ => { console . log ( 'caught error' ) ; return of ( null ) ; } ) , tap ( ( ) => this . loading$ . next ( true ) ) , switchMap ( ( [ category , timespan , metric , filter ] ) => { try { const response = this . getDashboardResponse ( category , timespan , metric , filter ) ; return response ; } catch ( err ) { return null ; } } ) , catchError ( _ => of ( null ) ) ) . subscribe ( response => { if ( ! response ) { return ; } const dashboard = response . dashboard ; this . ready$ . next ( true ) ; this . updateState ( { ... _state , category : dashboard . category , description : dashboard . description , timespan : dashboard . timespan , timespans : dashboard . timespans , filter : dashboard . filter , filters : dashboard . filters , metric : dashboard . metric , metrics : dashboard . metrics , } ) ; this . loading$ . next ( false ) ; } ) ; } getStateSnapshot ( ) { return { ... _state , timespans : { ... _state . timespans } , metrics : { ... _state . metrics } , filters : { ... _state . filters } , } ; } updateCategory ( category ) { this . updateState ( { ... _state , category , description : null , metrics : [ ] , } ) ; } updateTimespan ( timespan ) { this . updateState ( { ... _state , timespan , } ) ; } updateMetric ( metric ) { this . updateState ( { ... _state , metric } ) ; } updateFilter ( selectedFilterStr ) { if ( _state . filter . includes ( selectedFilterStr ) ) { return ; } const selectedFilterId = selectedFilterStr . split ( '::' ) [ 0 ] ; const filter = _state . filter ; const activeFilterIds = filter . map ( filterStr => { return filterStr . split ( '::' ) [ 0 ] ; } ) ; const filterIndex = activeFilterIds . findIndex ( filterId => filterId === selectedFilterId ) ; if ( activeFilterIds . includes ( selectedFilterId ) ) { filter . splice ( filterIndex , 1 , selectedFilterStr ) ; } else { filter . push ( selectedFilterStr ) ; } this . updateState ( { ... _state , filter } ) ; } /** Update internal state cache and emit from store... */ private updateState ( state ) { this . store . next ( ( _state = state ) ) ; } /** Dashboard REST call */ private getDashboardResponse ( category , timespan , metric , filter ) { this . loading$ . next ( true ) ; return this . http . get ( `api/v2/analytics/dashboards/${ category }` , { metric , timespan , filter : filter . join ( ) , } ) . pipe ( catchError ( _ => of ( null ) ) , map ( response => response ) ) ; } }