import { observable , makeObservable } from 'mobx' ; import { Executor , IExecutor , TaskScheduler } from '@cloudbeaver/core-executor' ; export abstract class LocalResource < TData , TParam > { data : TData ; readonly onDataOutdated : IExecutor < TParam > ; readonly onDataUpdate : IExecutor < TData > ; protected outdated = new Set < TParam > ( ) ; protected dataLoading = new Set < TParam > ( ) ; protected loading = false ; protected scheduler : TaskScheduler < TParam > ; constructor ( defaultValue ) { this . includes = this . includes . bind ( this ) ; this . scheduler = new TaskScheduler ( this . includes ) ; this . data = defaultValue ; this . onDataOutdated = new Executor ( null , this . includes ) ; this . onDataUpdate = new Executor ( ) ; this . loadingTask = this . loadingTask . bind ( this ) ; makeObservable < LocalResource < TData , TParam > , 'outdated' | 'dataLoading' | 'loading' > ( this , { data : observable , outdated : observable , dataLoading : observable , loading : observable , } ) ; } abstract isLoaded ( param ) ; isOutdated ( param ) { return this . outdated . has ( param ) ; } isLoading ( ) { return this . loading ; } isDataLoading ( key ) { return this . dataLoading . has ( key ) ; } markDataLoading ( key ) { this . dataLoading . add ( key ) ; } markDataLoaded ( key ) { this . dataLoading . delete ( key ) ; } markOutdated ( param ) { this . outdated . add ( param ) ; this . onDataOutdated . execute ( param ) ; } markUpdated ( param ) { this . outdated . delete ( param ) ; } async refresh ( param ) { await this . loadData ( param , true ) ; return this . data ; } async load ( param ) { await this . loadData ( param ) ; return this . data ; } protected includes ( param , second ) { return param === second ; } protected abstract loader ( param ) ; protected async performUpdate < T > ( param , update ) ; protected async performUpdate < T > ( param , update , exitCheck ) ; protected async performUpdate < T > ( param , update , exitCheck ? ) { if ( exitCheck ?. ( ) ) { return ; } return this . scheduler . schedule ( param , async ( ) => { if ( exitCheck ?. ( ) ) { return ; } return await this . taskWrapper ( param , update ) ; } ) ; } protected async loadData ( param , refresh ? ) { if ( this . isLoaded ( param ) && ! this . isOutdated ( param ) && ! refresh ) { return ; } await this . scheduler . schedule ( param , async ( ) => { if ( this . isLoaded ( param ) && ! this . isOutdated ( param ) && ! refresh ) { return ; } await this . taskWrapper ( param , this . loadingTask ) ; } ) ; } private async loadingTask ( param ) { this . data = await this . loader ( param ) ; } private async taskWrapper < T > ( param , promise ) { const prevState = this . loading ; this . loading = true ; this . markOutdated ( param ) ; try { const value = await promise ( param ) ; this . markUpdated ( param ) ; this . onDataUpdate . execute ( this . data ) ; return value ; } finally { this . loading = prevState ; } } }