import { AnyAction , applyMiddleware , createStore , Store } from 'redux' ; import mainReducer , { getConnectedUsersFromStore , getDeviceLayoutFromStore , getLoggedInUserFromStore , getScenarioFromStore , getTabletopFromStore , getTabletopIdFromStore , reduxFirstEnhancer , reduxFirstMiddleware , ReduxStoreType } from './mainReducer' ; import { isScenarioAction } from '../util/types' ; import { ScenarioReducerActionType , updateHeadActionIdsAction } from './scenarioReducer' ; import { setLastCommonScenarioAction } from './tabletopValidationReducer' ; import peerToPeerMiddleware from './peerToPeerMiddleware' ; import { addConnectedUserAction , ConnectedUserActionTypes , isAllowedUnverifiedAction , removeConnectedUserAction , updateSignalErrorAction } from './connectedUserReducer' ; import peerMessageHandler from '../util/peerMessageHandler' ; import { composeWithDevTools } from 'redux-devtools-extension' ; import thunk from 'redux-thunk' ; import { appVersion } from '../util/appVersion' ; import { getNetworkHubId , isTabletopLockedForPeer } from '../util/scenarioUtils' ; import { CommsNode } from '../util/commsNode' ; export default function buildStore ( ) { let store ; const onSentMessage = ( recipients , action ) => { if ( isScenarioAction ( action ) ) { store . dispatch ( updateHeadActionIdsAction ( action ) ) ; store . dispatch ( setLastCommonScenarioAction ( getScenarioFromStore ( store . getState ( ) ) , action as ScenarioReducerActionType ) ) ; } } ; const gTovePeerToPeerMiddleware = peerToPeerMiddleware < ReduxStoreType > ( { getCommsChannel : ( state ) => { const loggedInUser = getLoggedInUserFromStore ( state ) ; return { commsChannelId : loggedInUser && getTabletopFromStore ( state ) . gm && getTabletopIdFromStore ( state ) , commsStyle : getTabletopFromStore ( state ) . commsStyle , userId : loggedInUser ? loggedInUser . emailAddress : undefined } ; } , commsNodeOptions : { shouldDispatchLocally : ( action , state , commsNode ) => { const connectedUsers = getConnectedUsersFromStore ( state ) ; if ( action . fromPeerId && ( ! connectedUsers . users [ action . fromPeerId ] || ! connectedUsers . users [ action . fromPeerId ] . verifiedConnection ) && ! isAllowedUnverifiedAction ( action ) ) { return false ; } const tabletop = getTabletopFromStore ( state ) ; if ( isScenarioAction ( action ) && isTabletopLockedForPeer ( tabletop , connectedUsers . users , action . fromPeerId || ( commsNode ? commsNode . peerId : null ) , true ) ) { return false ; } if ( action . playersOnly || action . gmOnly ) { const user = getLoggedInUserFromStore ( state ) ; return ( user !== null && action . gmOnly === ( user . emailAddress === tabletop . gm ) ) ; } return true ; } , onEvents : { shouldConnect : ( peerNode , peerId , userId ) => { const myUserId = peerNode . userId ; const gmUserId = getTabletopFromStore ( store . getState ( ) ) . gm ; return ( myUserId !== gmUserId && userId === gmUserId ) || ( myUserId === gmUserId && userId !== undefined ) ; } , connect : async ( peerNode , peerId ) => { const state = store . getState ( ) ; const loggedInUser = getLoggedInUserFromStore ( state ) ! ; const deviceLayout = getDeviceLayoutFromStore ( state ) ; await peerNode . sendTo ( addConnectedUserAction ( peerNode . peerId , loggedInUser , appVersion , window . innerWidth , window . innerHeight , deviceLayout ) , { only : [ peerId ] } ) ; } , data : async ( peerNode , peerId , data ) => peerMessageHandler ( store , peerNode , peerId , data ) , close : async ( peerNode , peerId ) => { if ( ! peerNode . shutdown ) { store . dispatch ( removeConnectedUserAction ( peerId ) ) ; } } , signalError : async ( _peerNode , error ) => { const signalError = ( error !== '' ) ; if ( getConnectedUsersFromStore ( store . getState ( ) ) . signalError !== signalError ) { store . dispatch ( updateSignalErrorAction ( signalError ) ) ; } } } } , getSendToOptions : ( peerNode , action ) => { if ( action . type === ConnectedUserActionTypes . SET_USER_ALLOWED && action . allowed === false ) { peerNode . close ( action . peerId , 'You cannot connect to that tabletop.' ) ; } const peerKey = action . peerKey || action . meta ?. peerKey ; if ( peerKey ) { const state = store . getState ( ) ; const connectedUsers = getConnectedUsersFromStore ( state ) . users ; const tabletop = getTabletopFromStore ( state ) ; if ( isScenarioAction ( action ) && isTabletopLockedForPeer ( tabletop , connectedUsers , action . fromPeerId || peerNode . peerId , true ) ) { return undefined ; } const networkHubId = getNetworkHubId ( peerNode . userId , peerNode . peerId , tabletop . gm , connectedUsers ) ; let only ; if ( networkHubId === peerNode . peerId ) { only = Object . keys ( connectedUsers ) . filter ( ( peerId ) => ( connectedUsers [ peerId ] . verifiedConnection ) ) ; if ( action . gmOnly ) { only = only . filter ( ( peerId ) => ( connectedUsers [ peerId ] . verifiedGM ) ) ; } } else if ( networkHubId ) { only = [ networkHubId ] ; } const throttleKey = `${ action . type }.${ peerKey }` ; return { throttleKey , onSentMessage , only } ; } else { return undefined ; } } } ) ; store = createStore ( mainReducer , composeWithDevTools ( applyMiddleware ( thunk , reduxFirstMiddleware , gTovePeerToPeerMiddleware ) , reduxFirstEnhancer ) ) ; if ( module . hot ) { module . hot . accept ( './mainReducer' , ( ) => { store . replaceReducer ( require ( './mainReducer' ) . default ) ; } ) ; } return store ; }