import { Observable , Subject , BehaviorSubject , timer , of , combineLatest , Subscription , EMPTY } from 'rxjs' ; import { tap , delay , debounce , switchMap , takeUntil , finalize , filter } from 'rxjs/operators' ; import { NgProgressState , NgProgressConfig , ProgressConfig , ProgressState } from './ng-progress.interface' ; export class NgProgressRef { private readonly _state : BehaviorSubject < ProgressState > ; state : Observable < ProgressState > ; private readonly _config : BehaviorSubject < ProgressConfig > ; config : Observable < ProgressConfig > ; private readonly _started = new Subject < void > ( ) ; readonly started = this . _started . pipe ( filter ( ( ) => ! this . isStarted ) ) ; private readonly _completed = new Subject < void > ( ) ; readonly completed = this . _completed . pipe ( filter ( ( ) => this . isStarted ) ) ; private readonly _trickling = new Subject < boolean > ( ) ; private readonly _worker = Subscription . EMPTY ; private get snapshot ( ) : ProgressState { return this . _state . value ; } get isStarted ( ) : boolean { return this . snapshot . active ; } constructor ( customConfig , private _onDestroyCallback ) { this . _state = new BehaviorSubject < ProgressState > ( { active : false , value : 0 } ) ; this . _config = new BehaviorSubject < ProgressConfig > ( customConfig ) ; this . state = this . _state . asObservable ( ) ; this . config = this . _config . asObservable ( ) ; this . _worker = combineLatest ( [ this . _trickling , this . _config ] ) . pipe ( debounce ( ( [ start , config ] ) => timer ( start ? config . debounceTime : 0 ) ) , switchMap ( ( [ start , config ] ) => start ? this . onTrickling ( config ) : this . onComplete ( config ) ) ) . subscribe ( ) ; } /**
     * Start the progress
     */ start ( ) { this . _started . next ( ) ; this . _trickling . next ( true ) ; } /**
     * Complete the progress
     */ complete ( ) { this . _trickling . next ( false ) ; } /**
     * Increment the progress
     */ inc ( amount ? ) { const n = this . snapshot . value ; if ( ! this . isStarted ) { this . start ( ) ; } else { if ( typeof amount !== 'number' ) { amount = this . _config . value . trickleFunc ( n ) ; } this . set ( n + amount ) ; } } /**
     * Set the progress
     */ set ( n ) { this . setState ( { value : this . clamp ( n ) , active : true } ) ; } /**
     * Set config
     */ setConfig ( config ) { this . _config . next ( { ... this . _config . value , ... config } ) ; } /**
     * Destroy progress reference
     */ destroy ( ) { this . _worker . unsubscribe ( ) ; this . _trickling . complete ( ) ; this . _state . complete ( ) ; this . _config . complete ( ) ; this . _started . complete ( ) ; this . _completed . complete ( ) ; this . _onDestroyCallback ( ) ; } /**
     * Set progress state
     */ private setState ( state ) { this . _state . next ( { ... this . snapshot , ... state } ) ; } /**
     * Clamps a value to be between min and max
     */ private clamp ( n ) { return Math . max ( this . _config . value . min , Math . min ( this . _config . value . max , n ) ) ; } /**
     * Keeps incrementing the progress
     */ private onTrickling ( config ) { if ( ! this . isStarted ) { this . set ( this . _config . value . min ) ; } return timer ( 0 , config . trickleSpeed ) . pipe ( tap ( ( ) => this . inc ( ) ) ) ; } /**
     * Completes then resets the progress
     */ private onComplete ( config ) { this . _completed . next ( ) ; return ! this . isStarted ? EMPTY : of ( { } ) . pipe ( tap ( ( ) => this . setState ( { value : 100 } ) ) , delay ( config . speed * 1.7 ) , tap ( ( ) => this . setState ( { active : false } ) ) , delay ( config . speed ) , finalize ( ( ) => this . setState ( { value : 0 } ) ) , takeUntil ( this . _started ) ) ; } }