Skip to content

Instantly share code, notes, and snippets.

@melbourne2991
Created October 7, 2018 04:40
Show Gist options
  • Save melbourne2991/2e2886e30b3f06b0cb04d6887b888888 to your computer and use it in GitHub Desktop.
Save melbourne2991/2e2886e30b3f06b0cb04d6887b888888 to your computer and use it in GitHub Desktop.
// @flow
import * as R from "ramda";
import { Observable, BehaviorSubject, Subject, empty, of, merge } from "rxjs";
import {
scan,
tap,
share,
startWith,
mergeMap,
switchMap,
shareReplay,
filter,
map,
mapTo
} from "rxjs/operators";
type IReducer<T> = (state: T, action: {}) => T;
type IEffectFn = (actions$: any) => any;
type IEffect = { fn: IEffectFn, dispatch: boolean };
function testEffect($actions) {
return $actions.pipe(filter(val => val === "inc")).pipe(mapTo("dec"));
}
export function Effect(fn: IEffectFn, dispatch: boolean = true) {
return {
fn,
dispatch
};
}
export function createStore<T>(
reducer: IReducer<any>,
effects: IEffect[],
initialState: T
) {
const actions$ = new Subject();
const resetState$ = new BehaviorSubject(initialState);
const effectsOperator = function(source) {
return new Observable(subscriber => {
source.subscribe(subscriber);
effects &&
effects.forEach(effect => {
effect.fn(source).subscribe(val => actions$.next(val));
});
});
};
const state$ = resetState$
.pipe(
switchMap(state => {
return actions$
.pipe(effectsOperator)
.pipe(startWith(undefined))
.pipe(scan(reducer, state));
})
)
.pipe(shareReplay(1));
return {
actions$,
state$,
resetState$
};
}
export function trackHistory(store: any) {
let historyState = {
history: [],
head: 0
};
const currentHistory = hs => hs.history[hs.head - 1];
const incrementHead = R.over(R.lensProp("head"), R.inc);
const decrementHead = R.over(R.lensProp("head"), R.dec);
const addEntryToHistory = currentState =>
R.over(R.lensProp("history"), R.append(currentState));
const updateHistory = currentState =>
R.pipe(
addEntryToHistory(currentState),
incrementHead
)(historyState);
const splitHistory = R.set(
R.lensProp("history"),
R.take(historyState.head, historyState.history)
);
const state$ = store.state$.pipe(
tap(currentState => {
if (historyState.history.includes(currentState)) return;
if (historyState.head !== historyState.history.length) {
historyState = splitHistory(historyState);
}
historyState = updateHistory(currentState);
})
);
return {
...store,
state$,
canUndo: () => {
return !!historyState.history[historyState.head - 1];
},
canRedo: () => {
return !!historyState.history[historyState.head + 1];
},
undo: () => {
historyState = decrementHead(historyState);
store.resetState$.next(currentHistory(historyState));
},
redo: () => {
historyState = incrementHead(historyState);
store.resetState$.next(currentHistory(historyState));
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment