diff --git a/client/index.js b/client/index.js index 008e2a704b..5eef4fe032 100644 --- a/client/index.js +++ b/client/index.js @@ -9,6 +9,7 @@ import { hydrate } from 'thundercats'; import { render$ } from 'thundercats-react'; import { app$ } from '../common/app'; +import synchroniseHistory from './synchronise-history'; const debug = debugFactory('fcc:client'); const DOMContianer = document.getElementById('fcc'); @@ -23,18 +24,6 @@ const appLocation = createLocation( location.pathname + location.search ); -function location$(history) { - return Rx.Observable.create(function(observer) { - const dispose = history.listen(function(location) { - observer.onNext(location); - }); - - return Rx.Disposable.create(() => { - dispose(); - }); - }); -} - // returns an observable app$({ history, location: appLocation }) .flatMap( @@ -49,40 +38,22 @@ app$({ history, location: appLocation }) ({ nextLocation, props }, appCat) => ({ nextLocation, props, appCat }) ) .doOnNext(({ appCat }) => { - const appActions = appCat.getActions('appActions'); - const appStore = appCat.getStore('appStore'); + const { updateLocation, goTo, goBack } = appCat.getActions('appActions'); + const appStore$ = appCat.getStore('appStore'); - const route$ = location$(history) - .pluck('pathname') - .distinctUntilChanged(); + const routerState$ = appStore$ + .map(({ location }) => location) + .distinctUntilChanged( + location => location && location.key ? location.key : location + ); - appStore - .pluck('route') - .filter(route => !!route) - .withLatestFrom( - route$, - (nextRoute, currentRoute) => ({ currentRoute, nextRoute }) - ) - // only continue when route change requested - .filter(({ currentRoute, nextRoute }) => currentRoute !== nextRoute) - .doOnNext(({ nextRoute }) => { - debug('route change', nextRoute); - history.pushState(history.state, nextRoute); - }) - .subscribeOnError(err => console.error(err)); - - appActions.goBack.subscribe(function() { - history.goBack(); - }); - - appActions - .updateRoute - .pluck('route') - .doOnNext(route => { - debug('update route', route); - history.pushState(history.state, route); - }) - .subscribeOnError(err => console.error(err)); + synchroniseHistory( + history, + updateLocation, + goTo, + goBack, + routerState$ + ); }) .flatMap(({ props, appCat }) => { props.history = history; diff --git a/client/synchronise-history.js b/client/synchronise-history.js new file mode 100644 index 0000000000..3e36cf9747 --- /dev/null +++ b/client/synchronise-history.js @@ -0,0 +1,69 @@ +import { Disposable, Observable } from 'rx'; + +export function location$(history) { + return Observable.create(function(observer) { + const dispose = history.listen(function(location) { + observer.onNext(location); + }); + + return Disposable.create(() => { + dispose(); + }); + }); +} + +const emptyLocation = { + pathname: '', + search: '', + hash: '' +}; + +let prevKey; +let isSyncing = false; +export default function synchroniseHistory( + history, + updateLocation, + goTo, + goBack, + routerState$ +) { + routerState$.subscribe( + location => { + + if (!location) { + return null; + } + + // store location has changed, update history + if (location.key !== prevKey) { + isSyncing = true; + history.transitionTo({ ...emptyLocation, ...location }); + isSyncing = false; + } + } + ); + + location$(history) + .doOnNext(location => { + prevKey = location.key; + + if (isSyncing) { + return null; + } + + return updateLocation(location); + }) + .subscribe(() => {}); + + goTo + .doOnNext((route = '/') => { + history.push(route); + }) + .subscribe(() => {}); + + goBack + .doOnNext(() => { + history.goBack(); + }) + .subscribe(() => {}); +} diff --git a/common/app/flux/Actions.js b/common/app/flux/Actions.js index 83196b90f0..da2318f56d 100644 --- a/common/app/flux/Actions.js +++ b/common/app/flux/Actions.js @@ -38,8 +38,14 @@ export default Actions({ }); }, - updateRoute(route) { - return { route }; - }, - goBack: null + // routing + goTo: null, + goBack: null, + updateLocation(location) { + return { + transform(state) { + return { ...state, location }; + } + }; + } }); diff --git a/common/app/flux/Store.js b/common/app/flux/Store.js index e63c76cbc4..d7dc12e09f 100644 --- a/common/app/flux/Store.js +++ b/common/app/flux/Store.js @@ -20,7 +20,12 @@ export default Store({ value: initValue }, init({ instance: appStore, args: [cat] }) { - const { updateRoute, getUser, setTitle } = cat.getActions('appActions'); + const { + updateLocation, + getUser, + setTitle + } = cat.getActions('appActions'); + const register = createRegistrar(appStore); const { toggleQuestions, @@ -33,7 +38,17 @@ export default Store({ } = cat.getActions('hikesActions'); // app - register(setter(fromMany(getUser, setTitle, updateRoute))); + register( + fromMany( + setter( + fromMany( + getUser, + setTitle + ) + ), + updateLocation + ) + ); // hikes register( diff --git a/common/app/routes/Hikes/flux/Actions.js b/common/app/routes/Hikes/flux/Actions.js index cc07623d1b..4556308871 100644 --- a/common/app/routes/Hikes/flux/Actions.js +++ b/common/app/routes/Hikes/flux/Actions.js @@ -218,9 +218,12 @@ export default Actions({ const currentHike = findNextHike(hikes, id); // go to next route - state.route = currentHike && currentHike.dashedName ? - `/hikes/${ currentHike.dashedName }` : - '/hikes'; + state.location = { + action: 'PUSH', + pathname: currentHike && currentHike.dashedName ? + `/hikes/${ currentHike.dashedName }` : + '/hikes' + }; const hikesApp = { ...state.hikesApp,