Created
October 7, 2018 04:40
-
-
Save melbourne2991/2e2886e30b3f06b0cb04d6887b888888 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @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