From 585c79a738e0768ce53a4932dc5e5efc51168986 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Fri, 12 Apr 2019 16:22:57 +0200 Subject: [PATCH] fix: setInitialUrl - race condition - Do not set initialUrl if it is landingPage - Block "RouterAction.selectDevice" if currently selected device is in auth process - Move WalletActions.observe() before DiscoveryActions.restore() in "WalletSerivce" --- src/actions/RouterActions.js | 55 +++++++++++++++++++------------ src/services/WalletService.js | 61 +++++++++++++++++++---------------- 2 files changed, 67 insertions(+), 49 deletions(-) diff --git a/src/actions/RouterActions.js b/src/actions/RouterActions.js index d078baf1..4b7ad51c 100644 --- a/src/actions/RouterActions.js +++ b/src/actions/RouterActions.js @@ -430,29 +430,42 @@ export const setInitialUrl = (): PayloadAction => ( getState: GetState ): boolean => { const { initialPathname } = getState().wallet; - if (typeof initialPathname === 'string' && !dispatch(isLandingPageUrl(initialPathname, true))) { - const valid = dispatch( - getValidUrl({ - type: LOCATION_CHANGE, - payload: { - location: { - pathname: initialPathname, - hash: '', - search: '', - state: {}, - }, + if (typeof initialPathname !== 'string') return false; + + // DEVICE.CONNECT race condition, "selectDevice" method was called but currently selectedDevice is in getState (auth) process + // if so, consume this action (return true) to break "selectDevice" process + // "setInitialUrl" will be called again after AUTH_DEVICE action + const { selectedDevice } = getState().wallet; + if ( + selectedDevice && + selectedDevice.type === 'acquired' && + !selectedDevice.features.passphrase_protection && + !selectedDevice.state + ) + return true; + + const valid = dispatch( + getValidUrl({ + type: LOCATION_CHANGE, + payload: { + location: { + pathname: initialPathname, + hash: '', + search: '', + state: {}, }, - }) - ); + }, + }) + ); - if (valid === initialPathname) { - // reset initial url - dispatch({ - type: SET_INITIAL_URL, - }); - dispatch(goto(valid)); - return true; - } + if (valid === initialPathname) { + // reset initial url + dispatch({ + type: SET_INITIAL_URL, + }); + dispatch(goto(valid)); + return true; } + return false; }; diff --git a/src/services/WalletService.js b/src/services/WalletService.js index 569e03b6..5d0a75d9 100644 --- a/src/services/WalletService.js +++ b/src/services/WalletService.js @@ -29,11 +29,16 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa api.dispatch(WalletActions.init()); // set initial url // TODO: validate if initial url is potentially correct - api.dispatch({ - type: WALLET.SET_INITIAL_URL, - pathname: action.payload.location.pathname, - state: {}, - }); + // exclude landing page url + const { pathname } = action.payload.location; + const isValidPath = !api.dispatch(RouterActions.isLandingPageUrl(pathname, true)); + if (isValidPath) { + api.dispatch({ + type: WALLET.SET_INITIAL_URL, + pathname: action.payload.location.pathname, + state: {}, + }); + } // pass action and break process return next(action); } @@ -63,6 +68,29 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa // update common values ONLY if application is ready if (!api.getState().wallet.ready) return action; + // observe common values in WallerReducer + if (!(await api.dispatch(WalletActions.observe(prevState, action)))) { + // if "selectedDevice" didn't change observe common values in SelectedAccountReducer + if (!(await api.dispatch(SelectedAccountActions.observe(prevState, action)))) { + // if "selectedAccount" didn't change observe send form props changes + api.dispatch(SendFormActions.observe(prevState, action)); + } + } else { + // no changes in common values + if (action.type === CONNECT.RECEIVE_WALLET_TYPE) { + if (action.device.state) { + // redirect to root view (Dashboard) if device was authenticated before + api.dispatch(RouterActions.selectFirstAvailableDevice(true)); + } + api.dispatch(TrezorConnectActions.authorizeDevice()); + } + if (action.type === CONNECT.AUTH_DEVICE) { + // selected device did changed + // try to restore discovery after device authentication + api.dispatch(DiscoveryActions.restore()); + } + } + // double verification needed // Corner case: LOCATION_CHANGE was called but pathname didn't changed (redirection from RouterService) const prevLocation = prevState.router.location; @@ -96,29 +124,6 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa api.dispatch(NotificationActions.clear(prevLocation.state, currentLocation.state)); } - // observe common values in WallerReducer - if (!(await api.dispatch(WalletActions.observe(prevState, action)))) { - // if "selectedDevice" didn't change observe common values in SelectedAccountReducer - if (!(await api.dispatch(SelectedAccountActions.observe(prevState, action)))) { - // if "selectedAccount" didn't change observe send form props changes - api.dispatch(SendFormActions.observe(prevState, action)); - } - } else { - // no changes in common values - if (action.type === CONNECT.RECEIVE_WALLET_TYPE) { - if (action.device.state) { - // redirect to root view (Dashboard) if device was authenticated before - api.dispatch(RouterActions.selectFirstAvailableDevice(true)); - } - api.dispatch(TrezorConnectActions.authorizeDevice()); - } - if (action.type === CONNECT.AUTH_DEVICE) { - // selected device did changed - // try to restore discovery after device authentication - api.dispatch(DiscoveryActions.restore()); - } - } - // even if "selectedDevice" didn't change because it was updated on DEVICE.CHANGED before DEVICE.CONNECT action // try to restore discovery if (action.type === DEVICE.CONNECT) {