import * as plugins from './smartstate.plugins.js'; import { StateAction, IActionDef } from './smartstate.classes.stateaction.js'; export class StatePart { public name: TStatePartName; public state = new plugins.smartrx.rxjs.Subject(); public stateStore: TStatePayload; private cumulativeDeferred = plugins.smartpromise.cumulativeDefer(); constructor(nameArg: TStatePartName) { this.name = nameArg; } /** * gets the state from the state store */ public getState(): TStatePayload { return this.stateStore; } /** * sets the stateStore to the new state * @param newStateArg */ public setState(newStateArg: TStatePayload) { this.stateStore = newStateArg; this.notifyChange(); } /** * notifies of a change on the state */ public notifyChange() { const createStateHash = (stateArg: any) => { return plugins.isohash.sha256FromString(plugins.smartjson.stringify(stateArg)); }; if ( this.stateStore && this.lastStateNotificationPayloadHash && createStateHash(this.stateStore) === createStateHash(this.lastStateNotificationPayloadHash) ) { return; } else { this.lastStateNotificationPayloadHash = this.stateStore; } this.state.next(this.stateStore); } private lastStateNotificationPayloadHash: any; /** * selects a state or a substate */ public select( selectorFn?: (state: TStatePayload) => T ): plugins.smartrx.rxjs.Observable { if (!selectorFn) { selectorFn = (state: TStatePayload) => (state); } const mapped = this.state.pipe( plugins.smartrx.rxjs.ops.startWith(this.getState()), plugins.smartrx.rxjs.ops.map((stateArg) => { try { return selectorFn(stateArg); } catch (e) { // Nothing here } }) ); return mapped; } /** * creates an action capable of modifying the state */ public createAction( actionDef: IActionDef ): StateAction { return new StateAction(this, actionDef); } /** * dispatches an action on the statepart level */ public async dispatchAction(stateAction: StateAction, actionPayload: T) { await this.cumulativeDeferred.promise; const newState = await stateAction.actionDef(this, actionPayload); this.setState(newState); } /** * waits until a certain part of the state becomes available * @param selectorFn */ public async waitUntilPresent( selectorFn?: (state: TStatePayload) => T ): Promise { const done = plugins.smartpromise.defer(); const selectedObservable = this.select(selectorFn); const subscription = selectedObservable.subscribe(async (value) => { if (value) { done.resolve(value); } }); const result = await done.promise; subscription.unsubscribe(); return result; } /** * is executed */ public stateSetup( funcArg: (statePartArg?: StatePart) => Promise ) { this.cumulativeDeferred.addPromise(funcArg(this)); } }