import { action , makeObservable , observable , runInAction } from 'mobx' ; import { getDependingDataActions } from './Actions/DatabaseDataActionDecorator' ; import { isDatabaseDataAction } from './DatabaseDataAction' ; import type { IDatabaseDataAction , IDatabaseDataActionClass , IDatabaseDataActionInterface } from './IDatabaseDataAction' ; import type { IDatabaseDataActions } from './IDatabaseDataActions' ; import type { IDatabaseDataResult } from './IDatabaseDataResult' ; import type { IDatabaseDataSource } from './IDatabaseDataSource' ; type ActionsList < TOptions , TResult extends IDatabaseDataResult > = Array < IDatabaseDataAction < TOptions , TResult > > ; export class DatabaseDataActions < TOptions , TResult extends IDatabaseDataResult > implements IDatabaseDataActions < TOptions , TResult > { private actions : Map < string , ActionsList < TOptions , TResult > > ; private source : IDatabaseDataSource < TOptions , TResult > ; constructor ( source ) { this . actions = new Map ( ) ; this . source = source ; makeObservable < this , 'actions' | 'createActionsList' > ( this , { actions : observable . shallow , updateResults : action , createActionsList : action , } ) ; } tryGet < T extends IDatabaseDataAction < TOptions , TResult > > ( result , Action ) { if ( Action . dataFormat && ! Action . dataFormat . includes ( result . dataFormat ) ) { return undefined ; } return this . get ( result , Action ) ; } get < T extends IDatabaseDataAction < TOptions , TResult > > ( result , Action ) { if ( Action . dataFormat && ! Action . dataFormat . includes ( result . dataFormat ) ) { throw new Error ( 'DataFormat unsupported' ) ; } const actions = this . getOrCreateActionsList ( result . uniqueResultId ) ; return runInAction ( ( ) => { let action = actions . find ( action => action instanceof Action ) ; if ( ! action ) { const allDeps = getDependingDataActions ( Action ) . slice ( 2 ) ; const depends = allDeps . filter ( isDatabaseDataAction ) . map ( action => this . get < IDatabaseDataAction < TOptions , TResult > > ( result , action ) ) ; if ( allDeps . length !== depends . length ) { throw new Error ( 'Unsupported inject in: ' + Action . name ) ; } action = new Action ( this . source , result , ... depends ) ; this . addActionToList ( result . uniqueResultId , actions , action ) ; } return action as T ; } ) ; } getImplementation < T extends IDatabaseDataAction < TOptions , TResult > > ( result , Action ) { const actions = this . getActionsList ( result . uniqueResultId ) ; const action = actions ?. find ( action => action instanceof Action ) ; return action as T | undefined ; } updateResults ( results ) { let actionsMap = Array . from ( this . actions . entries ( ) ) ; for ( const [ key , actions ] of actionsMap ) { const result = results . find ( result => result . uniqueResultId === key ) ; if ( ! result ) { for ( const action of actions ) { action . dispose ( ) ; } this . actions . delete ( key ) ; } else { for ( const action of actions ) { action . updateResult ( result ) ; } } } actionsMap = Array . from ( this . actions . entries ( ) ) ; for ( const [ , actions ] of actionsMap ) { for ( const action of actions ) { action . afterResultUpdate ( ) ; } } } private addActionToList ( resultId , actions , action ) { actions . push ( action ) ; } private getOrCreateActionsList ( resultId ) { let actions = this . getActionsList ( resultId ) ; if ( ! actions ) { actions = this . createActionsList ( resultId ) ; } return actions ; } private createActionsList ( resultId ) { const actions = observable . array ( [ ] , { deep : false } ) ; this . actions . set ( resultId , actions ) ; return actions ; } private getActionsList ( resultId ) { return this . actions . get ( resultId ) ; } }