smartstate/ts/smartstate.classes.statepart.ts

98 lines
2.5 KiB
TypeScript
Raw Permalink Normal View History

2019-02-21 20:48:39 +00:00
import * as plugins from './smartstate.plugins';
import { Observable, Subject } from 'rxjs';
import { startWith, takeUntil, map } from 'rxjs/operators';
2019-02-26 17:09:38 +00:00
import { StateAction, IActionDef } from './smartstate.classes.stateaction';
2019-02-21 20:48:39 +00:00
2019-02-26 17:09:38 +00:00
export class StatePart<TStatePartName, TStatePayload> {
2019-09-25 13:28:39 +00:00
public name: TStatePartName;
public state = new Subject<TStatePayload>();
public stateStore: TStatePayload;
2019-02-21 20:48:39 +00:00
2019-02-26 17:09:38 +00:00
constructor(nameArg: TStatePartName) {
2019-02-21 20:48:39 +00:00
this.name = nameArg;
}
/**
* gets the state from the state store
*/
2019-09-25 13:28:39 +00:00
public getState(): TStatePayload {
2019-02-21 20:48:39 +00:00
return this.stateStore;
}
/**
* sets the stateStore to the new state
* @param newStateArg
*/
2019-09-25 13:28:39 +00:00
public setState(newStateArg: TStatePayload) {
2019-02-21 20:48:39 +00:00
this.stateStore = newStateArg;
this.notifyChange();
}
/**
* notifies of a change on the state
*/
2019-09-25 13:28:39 +00:00
public notifyChange() {
2019-02-21 20:48:39 +00:00
this.state.next(this.stateStore);
}
/**
* selects a state or a substate
*/
2019-09-25 13:28:39 +00:00
public select<T = TStatePayload>(selectorFn?: (state: TStatePayload) => T): Observable<T> {
2019-02-21 20:48:39 +00:00
if (!selectorFn) {
2019-02-26 17:09:38 +00:00
selectorFn = (state: TStatePayload) => <T>(<any>state);
2019-02-21 20:48:39 +00:00
}
const mapped = this.state.pipe(
startWith(this.getState()),
2020-05-18 04:10:36 +00:00
map(stateArg => {
2019-09-25 13:28:39 +00:00
try {
return selectorFn(stateArg);
} catch (e) {
// Nothing here
}
})
2019-02-21 20:48:39 +00:00
);
return mapped;
}
2019-02-26 17:09:38 +00:00
/**
* creates an action capable of modifying the state
*/
2019-09-25 13:28:39 +00:00
public createAction<TActionPayload>(
2019-02-27 01:00:47 +00:00
actionDef: IActionDef<TStatePayload, TActionPayload>
): StateAction<TStatePayload, TActionPayload> {
2019-09-25 13:28:39 +00:00
return new StateAction(this, actionDef);
2019-02-26 17:09:38 +00:00
}
2019-02-21 20:48:39 +00:00
/**
* dispatches an action on the statepart level
*/
2019-09-25 13:28:39 +00:00
public async dispatchAction<T>(stateAction: StateAction<TStatePayload, T>, actionPayload: T) {
2019-02-26 17:09:38 +00:00
const newState = await stateAction.actionDef(this, actionPayload);
2019-02-21 20:48:39 +00:00
this.setState(newState);
}
2019-09-25 13:28:39 +00:00
/**
* waits until a certain part of the state becomes available
* @param selectorFn
*/
2020-05-18 04:10:36 +00:00
public async waitUntilPresent<T = TStatePayload>(
selectorFn?: (state: TStatePayload) => T
): Promise<T> {
2019-09-25 13:28:39 +00:00
const done = plugins.smartpromise.defer<T>();
const selectedObservable = this.select(selectorFn);
2019-09-25 15:09:35 +00:00
const subscription = selectedObservable.subscribe(async value => {
2019-09-25 13:28:39 +00:00
if (value) {
done.resolve(value);
}
});
2019-09-25 15:09:35 +00:00
const result = await done.promise;
subscription.unsubscribe();
return result;
2019-09-25 13:28:39 +00:00
}
2019-02-21 20:48:39 +00:00
}