import * as plugins from './smartstate.plugins'; import { Observable, Subject } from 'rxjs'; import { startWith, takeUntil, map } from 'rxjs/operators'; import { StateAction, IActionDef } from './smartstate.classes.stateaction'; export class StatePart { public name: TStatePartName; public state = new Subject(); public stateStore: TStatePayload; 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() { this.state.next(this.stateStore); } /** * selects a state or a substate */ public select(selectorFn?: (state: TStatePayload) => T): Observable { if (!selectorFn) { selectorFn = (state: TStatePayload) => (state); } const mapped = this.state.pipe( startWith(this.getState()), 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) { 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(value => { if (value) { subscription.unsubscribe(); done.resolve(value); } }); return await done.promise; } }