import type { History , Location } from 'history' ; import type { History as ReachHistory } from '@reach/router' ; import type { Middleware , Reducer , Store } from 'redux' ; import { go , goBack , goForward , back , forward , push , replace , locationChangeAction , } from './actions' ; import { createRouterMiddleware } from './middleware' ; import { createRouterReducer , RouterState } from './reducer' ; export interface IHistoryContextOptions { history : History ; routerReducerKey ? : string ; oldLocationChangePayload ? : boolean ; reduxTravelling ? : boolean ; showHistoryAction ? : boolean ; selectRouterState ? : < S > ( state : S ) => RouterState ; savePreviousLocations ? : number ; batch ? : ( callback : ( ) => void ) => void ; reachGlobalHistory ? : ReachHistory ; } export interface IHistoryContext { createReduxHistory : ( store : Store ) => History & { listenObject : boolean ; } ; routerMiddleware : Middleware ; routerReducer : Reducer < RouterState > ; } export const createReduxHistoryContext = ( { history , routerReducerKey = 'router' , reduxTravelling = false , showHistoryAction = false , selectRouterState , savePreviousLocations = 0 , batch , reachGlobalHistory , } ) : IHistoryContext => { let listenObject = false ; const callListener = ( listener , location , action ) => listenObject ? listener ( { location , action } ) : listener ( location , action ) ; if ( typeof batch !== 'function' ) { batch = fn => { fn ( ) ; } ; } /** ********************************************  REDUX REDUCER ***************************************************** */ if ( typeof selectRouterState !== 'function' ) { selectRouterState = state => state [ routerReducerKey ] ; } const routerReducer = createRouterReducer ( { savePreviousLocations } ) ; const routerMiddleware = createRouterMiddleware ( { history , showHistoryAction } ) ; /** ******************************************  REDUX TRAVELLING  ************************************************** */ let isReduxTravelling = false ; const handleReduxTravelling = ( store ) => { const locationEqual = ( loc1 , loc2 ) => loc1 . pathname === loc2 . pathname && loc1 . search === loc2 . search && loc1 . hash === loc2 . hash ; return store . subscribe ( ( ) => { const sLoc = selectRouterState ( store . getState ( ) ) . location ; const hLoc = history . location ; if ( sLoc && hLoc && ! locationEqual ( sLoc , hLoc ) ) { isReduxTravelling = true ; history . push ( { pathname : sLoc . pathname , search : sLoc . search , hash : sLoc . hash } ) ; } } ) ; } ; /** ******************************************  REDUX FIRST HISTORY   *********************************************** */ const createReduxHistory = ( store ) : History & { listenObject : boolean ; } => { let registeredCallback = [ ] ; store . dispatch ( locationChangeAction ( history . location , history . action ) ) ; if ( reduxTravelling ) { handleReduxTravelling ( store ) ; } history . listen ( ( location , action ) => { if ( location . location ) { action = location . action ; location = location . location ; listenObject = true ; } if ( isReduxTravelling ) { isReduxTravelling = false ; const routerState = selectRouterState ( store . getState ( ) ) ; registeredCallback . forEach ( c => callListener ( c , routerState . location , routerState . action ) ) ; return ; } batch ( ( ) => { store . dispatch ( locationChangeAction ( location , action ) ) ; const routerState = selectRouterState ( store . getState ( ) ) ; registeredCallback . forEach ( c => callListener ( c , routerState . location , routerState . action ) ) ; } ) ; } ) ; if ( reachGlobalHistory ) { reachGlobalHistory . listen ( ( { location , action } ) => { if ( action !== `POP` ) { const loc = { pathname : location . pathname , search : location . search , hash : location . hash , key : location . key , state : location . state , } ; batch ( ( ) => { store . dispatch ( locationChangeAction ( loc , action ) ) ; const routerState = selectRouterState ( store . getState ( ) ) ; registeredCallback . forEach ( c => callListener ( c , routerState . location , routerState . action ) ) ; } ) ; } } ) ; } return { block : history . block , createHref : history . createHref , push : ( ... args ) => store . dispatch ( push ( ... args ) ) , replace : ( ... args ) => store . dispatch ( replace ( ... args ) ) , go : ( ... args ) => store . dispatch ( go ( ... args ) ) , goBack : ( ... args ) => store . dispatch ( goBack ( ... args ) ) , goForward : ( ... args ) => store . dispatch ( goForward ( ... args ) ) , back : ( ... args ) => store . dispatch ( back ( ... args ) ) , forward : ( ... args ) => store . dispatch ( forward ( ... args ) ) , listen : callback => { if ( registeredCallback . indexOf ( callback ) < 0 ) { registeredCallback . push ( callback ) ; } return ( ) => { registeredCallback = registeredCallback . filter ( c => c !== callback ) ; } ; } , get location ( ) { return selectRouterState ( store . getState ( ) ) . location ; } , get action ( ) { return selectRouterState ( store . getState ( ) ) . action ; } , get length ( ) { return history . length ; } , get listenObject ( ) { return listenObject ; } , } ; } ; return { routerReducer , routerMiddleware , createReduxHistory } ; } ;