import { Component } from '@angular/core' ; import { FormBuilder , FormControl } from '@angular/forms' ; import { ActivatedRoute , Router } from '@angular/router' ; import { RxState } from '@rx-angular/state' ; import { Observable , of , Subject } from 'rxjs' ; import { catchError , debounceTime , distinctUntilChanged , map , mapTo , switchMap , take , tap , filter , } from 'rxjs/operators' ; import { Song } from 'src/@types/song' ; import { MusickitService } from '../../providers/musickit-service/musickit-service.service' ; import { PlayerService } from '../../providers/player/player.service2' ; interface ICollection { songs ? : any [ ] ; albums ? : any [ ] ; playlists ? : any [ ] ; } interface ISearchPageState { hasError : boolean ; isLoading : boolean ; collection : Partial < ICollection > ; } const stateFixtures = { idle : { isLoading : false , collection : null , hasError : false } , error : { isLoading : false , hasError : true } , loading : { isLoading : true } , success : { isLoading : false , hasError : false } , } ; const idleState = ( state ) => ( { ... state , ... stateFixtures . idle , } ) ; const errorState = ( state ) => ( { ... state , ... stateFixtures . error , } ) ; const loadingState = ( state ) => ( { ... state , ... stateFixtures . loading , } ) ; const successState = ( state , val ) => ( { ... state , ... stateFixtures . success , collection : val , } ) ; @ Component ( { selector : 'app-search-page' , templateUrl : './search.page.html' , styleUrls : [ './search.page.scss' ] , providers : [ RxState ] , } ) export class SearchPage { public state$ : Observable < ISearchPageState > = this . stateService . select ( ) ; public searchForm = this . fb . group ( { search : '' } ) ; public searchClearTrigger$ = new Subject ( ) ; public playSongTrigger$ = new Subject ( ) ; public segmentFilter = new FormControl ( 'songs' ) ; private segmentFilterUpdate = this . segmentFilter . valueChanges . pipe ( tap ( ( val ) => console . log ( val ) ) ) ; private searchTerm$ = this . searchForm . valueChanges . pipe ( map ( ( { search } ) => search ) , distinctUntilChanged ( ) ) ; private writeUrlEffect$ = this . searchTerm$ . pipe ( debounceTime ( 16 ) , tap ( ( term ) => { const navExtras = term ? { queryParams : { query : term } } : { } ; this . router . navigate ( [ ] , { ... navExtras } ) ; } ) ) ; private readUrlEffect$ = this . route . queryParams . pipe ( map ( ( { query } ) => this . searchForm . setValue ( { search : query ?? '' } ) ) ) ; private clearCollection$ = this . searchTerm$ . pipe ( filter ( ( term ) => ! term ) , mapTo ( idleState ( { } ) ) ) ; private fetchDataSideEffect$ = this . searchTerm$ . pipe ( filter ( ( term ) => ! ! term ) , debounceTime ( 500 ) , tap ( ( ) => this . stateService . set ( loadingState . bind ( this ) ) ) , switchMap ( ( term ) => this . api . search ( term ) ) , map ( ( results ) => successState ( { } , results ) ) , catchError ( ( ) => of ( errorState . bind ( this ) ) ) ) ; constructor ( private stateService , private api , private player , private router , private route , private fb ) { this . stateService . set ( idleState ) ; this . stateService . hold ( this . playSongTrigger$ . pipe ( tap ( this . playSong . bind ( this ) ) ) ) ; this . stateService . connect ( this . searchClearTrigger$ . pipe ( mapTo ( idleState . bind ( this ) ) ) ) ; this . stateService . connect ( this . clearCollection$ ) ; this . stateService . connect ( this . segmentFilterUpdate ) ; this . stateService . hold ( this . readUrlEffect$ . pipe ( take ( 1 ) ) ) ; this . stateService . hold ( this . writeUrlEffect$ ) ; this . stateService . connect ( this . fetchDataSideEffect$ ) ; } playSong ( song ) { this . player . setQueueFromItems ( [ song ] ) ; } }