From 1b6143e457691342564cabcf25a99f67acd3825a Mon Sep 17 00:00:00 2001 From: William O'Beirne Date: Sat, 24 Feb 2018 20:32:34 -0500 Subject: [PATCH] Don't refresh on network change (#1144) * Configure sagas and components to react to network switch. * tscheck * Update tests with new behavior. * tscheck * Log out of wallet on network change. * Fix up test. --- .../BalanceSidebar/EquivalentValues.tsx | 8 +++++--- .../TXMetaDataPanel/TXMetaDataPanel.tsx | 5 ++++- .../WalletDecrypt/components/LedgerNano.tsx | 6 ++++++ .../WalletDecrypt/components/Mnemonic.tsx | 12 +++++++++-- .../WalletDecrypt/components/Trezor.tsx | 6 ++++++ common/sagas/config/node.ts | 20 +++++++------------ common/sagas/transaction/network/nonce.ts | 5 +---- spec/reducers/config/config.spec.ts | 7 +++---- spec/sagas/transaction/network/nonce.spec.ts | 13 ++++-------- 9 files changed, 46 insertions(+), 36 deletions(-) diff --git a/common/components/BalanceSidebar/EquivalentValues.tsx b/common/components/BalanceSidebar/EquivalentValues.tsx index cc4b2499..4fe011d6 100644 --- a/common/components/BalanceSidebar/EquivalentValues.tsx +++ b/common/components/BalanceSidebar/EquivalentValues.tsx @@ -81,11 +81,12 @@ class EquivalentValues extends React.Component { } public componentWillReceiveProps(nextProps: Props) { - const { balance, tokenBalances, isOffline } = this.props; + const { balance, tokenBalances, isOffline, network } = this.props; if ( nextProps.balance !== balance || nextProps.tokenBalances !== tokenBalances || - nextProps.isOffline !== isOffline + nextProps.isOffline !== isOffline || + nextProps.network.unit !== network.unit ) { const defaultOption = this.defaultOption( nextProps.balance, @@ -288,7 +289,8 @@ class EquivalentValues extends React.Component { const currencies = tokenBalances .filter(tk => !tk.balance.isZero()) .map(tk => tk.symbol) - .sort(); + .sort() + .concat([props.network.unit]); // If it's the same currencies as we have, skip it if (this.requestedCurrencies && currencies.join() === this.requestedCurrencies.join()) { diff --git a/common/components/TXMetaDataPanel/TXMetaDataPanel.tsx b/common/components/TXMetaDataPanel/TXMetaDataPanel.tsx index 05432376..8f2f48fb 100644 --- a/common/components/TXMetaDataPanel/TXMetaDataPanel.tsx +++ b/common/components/TXMetaDataPanel/TXMetaDataPanel.tsx @@ -76,7 +76,10 @@ class TXMetaDataPanel extends React.Component { } public componentWillReceiveProps(nextProps: Props) { - if (this.props.offline && !nextProps.offline) { + if ( + (this.props.offline && !nextProps.offline) || + this.props.network.unit !== nextProps.network.unit + ) { this.props.fetchCCRates([this.props.network.unit]); } if (this.props.gasPrice !== nextProps.gasPrice) { diff --git a/common/components/WalletDecrypt/components/LedgerNano.tsx b/common/components/WalletDecrypt/components/LedgerNano.tsx index fd7da02c..0aa5b392 100644 --- a/common/components/WalletDecrypt/components/LedgerNano.tsx +++ b/common/components/WalletDecrypt/components/LedgerNano.tsx @@ -46,6 +46,12 @@ class LedgerNanoSDecryptClass extends PureComponent { }); }; + public componentWillReceiveProps(nextProps: Props) { + if (this.props.dPath !== nextProps.dPath) { + this.setState({ dPath: nextProps.dPath.value }); + } + } + public render() { const { dPath, publicKey, chainCode, error, isLoading, showTip } = this.state; const showErr = error ? 'is-showing' : ''; diff --git a/common/components/WalletDecrypt/components/Mnemonic.tsx b/common/components/WalletDecrypt/components/Mnemonic.tsx index c24fb25c..bee3cd6e 100644 --- a/common/components/WalletDecrypt/components/Mnemonic.tsx +++ b/common/components/WalletDecrypt/components/Mnemonic.tsx @@ -9,7 +9,7 @@ import { connect } from 'react-redux'; import { getSingleDPath, getPaths } from 'selectors/config/wallet'; import { TogglablePassword } from 'components'; -interface Props { +interface OwnProps { onUnlock(param: any): void; } @@ -18,6 +18,8 @@ interface StateProps { dPaths: DPath[]; } +type Props = OwnProps & StateProps; + interface State { phrase: string; formattedPhrase: string; @@ -26,7 +28,7 @@ interface State { dPath: string; } -class MnemonicDecryptClass extends PureComponent { +class MnemonicDecryptClass extends PureComponent { public state: State = { phrase: '', formattedPhrase: '', @@ -35,6 +37,12 @@ class MnemonicDecryptClass extends PureComponent { dPath: this.props.dPath.value }; + public componentWillReceiveProps(nextProps: Props) { + if (this.props.dPath !== nextProps.dPath) { + this.setState({ dPath: nextProps.dPath.value }); + } + } + public render() { const { phrase, formattedPhrase, seed, dPath, pass } = this.state; const isValidMnemonic = validateMnemonic(formattedPhrase); diff --git a/common/components/WalletDecrypt/components/Trezor.tsx b/common/components/WalletDecrypt/components/Trezor.tsx index 9be4d5a8..97cebbf5 100644 --- a/common/components/WalletDecrypt/components/Trezor.tsx +++ b/common/components/WalletDecrypt/components/Trezor.tsx @@ -40,6 +40,12 @@ class TrezorDecryptClass extends PureComponent { isLoading: false }; + public componentWillReceiveProps(nextProps: Props) { + if (this.props.dPath !== nextProps.dPath) { + this.setState({ dPath: nextProps.dPath.value }); + } + } + public render() { const { dPath, publicKey, chainCode, error, isLoading } = this.state; const showErr = error ? 'is-showing' : ''; diff --git a/common/sagas/config/node.ts b/common/sagas/config/node.ts index 23029509..367c2586 100644 --- a/common/sagas/config/node.ts +++ b/common/sagas/config/node.ts @@ -30,10 +30,10 @@ import { ChangeNodeIntentAction } from 'actions/config'; import { showNotification } from 'actions/notifications'; +import { resetWallet } from 'actions/wallet'; import { translateRaw } from 'translations'; import { StaticNodeConfig, CustomNodeConfig, NodeConfig } from 'types/node'; import { CustomNetworkConfig, StaticNetworkConfig } from 'types/network'; -import { Web3Service } from 'libs/nodes/web3'; let hasCheckedOnline = false; export function* pollOfflineStatus(): SagaIterator { @@ -168,18 +168,8 @@ export function* handleNodeChangeIntent({ yield put(setLatestBlock(currentBlock)); yield put(changeNode({ networkId: nextNodeConfig.network, nodeId: nodeIdToSwitchTo })); - // TODO - re-enable once DeterministicWallet state is fixed to flush properly. - // DeterministicWallet keeps path related state we need to flush before we can stop reloading - - // const currentWallet: IWallet | null = yield select(getWalletInst); - // if there's no wallet, do not reload as there's no component state to resync - // if (currentWallet && currentConfig.network !== actionConfig.network) { - - const isNewNetwork = currentConfig.network !== nextNodeConfig.network; - const newIsWeb3 = nextNodeConfig.service === Web3Service; - // don't reload when web3 is selected; node will automatically re-set and state is not an issue here - if (isNewNetwork && !newIsWeb3) { - yield call(reload); + if (currentConfig.network !== nextNodeConfig.network) { + yield fork(handleNewNetwork); } } @@ -187,6 +177,10 @@ export function* switchToNewNode(action: AddCustomNodeAction): SagaIterator { yield put(changeNodeIntent(action.payload.id)); } +export function* handleNewNetwork() { + yield put(resetWallet()); +} + export const node = [ takeEvery(TypeKeys.CONFIG_NODE_CHANGE_INTENT, handleNodeChangeIntent), takeLatest(TypeKeys.CONFIG_POLL_OFFLINE_STATUS, handlePollOfflineStatus), diff --git a/common/sagas/transaction/network/nonce.ts b/common/sagas/transaction/network/nonce.ts index 2f3f7ead..4eb68a57 100644 --- a/common/sagas/transaction/network/nonce.ts +++ b/common/sagas/transaction/network/nonce.ts @@ -14,13 +14,10 @@ export function* handleNonceRequest(): SagaIterator { const walletInst: AppState['wallet']['inst'] = yield select(getWalletInst); const isOffline: boolean = yield select(getOffline); try { - if (isOffline) { + if (isOffline || !walletInst) { return; } - if (!walletInst) { - throw Error(); - } const fromAddress: string = yield apply(walletInst, walletInst.getAddressString); const retrievedNonce: string = yield apply(nodeLib, nodeLib.getTransactionCount, [fromAddress]); diff --git a/spec/reducers/config/config.spec.ts b/spec/reducers/config/config.spec.ts index c5a03304..508e9b4a 100644 --- a/spec/reducers/config/config.spec.ts +++ b/spec/reducers/config/config.spec.ts @@ -7,7 +7,7 @@ import { handleNodeChangeIntent, handlePollOfflineStatus, pollOfflineStatus, - reload + handleNewNetwork } from 'sagas/config/node'; import { getNodeId, @@ -213,9 +213,8 @@ describe('handleNodeChangeIntent*', () => { ); }); - it('should call reload if network is new', () => { - expect(data.gen.next().value).toEqual(call(reload)); - expect(data.gen.next().done).toEqual(true); + it('should fork handleNewNetwork', () => { + expect(data.gen.next().value).toEqual(fork(handleNewNetwork)); }); it('should be done', () => { diff --git a/spec/sagas/transaction/network/nonce.spec.ts b/spec/sagas/transaction/network/nonce.spec.ts index 596d6fc1..caad6a5d 100644 --- a/spec/sagas/transaction/network/nonce.spec.ts +++ b/spec/sagas/transaction/network/nonce.spec.ts @@ -1,8 +1,7 @@ -import { getNonceSucceeded, getNonceFailed, inputNonce } from 'actions/transaction'; +import { getNonceSucceeded, inputNonce } from 'actions/transaction'; import { apply, put, select, fork, take, cancel } from 'redux-saga/effects'; import { getNodeLib, getOffline } from 'selectors/config'; import { getWalletInst } from 'selectors/wallet'; -import { showNotification } from 'actions/notifications'; import { handleNonceRequest, handleNonceRequestWrapper } from 'sagas/transaction/network/nonce'; import { cloneableGenerator, createMockTask } from 'redux-saga/utils'; import { TypeKeys as WalletTK } from 'actions/wallet'; @@ -41,14 +40,10 @@ describe('handleNonceRequest*', () => { expect(gens.gen.next(nodeLib).value).toEqual(select(getWalletInst)); }); - it('should handle being called without wallet inst correctly', () => { + it('should exit if being called without a wallet inst', () => { gens.noWallet = gens.gen.clone(); - gens.noWallet.next(); - expect(gens.noWallet.next(offline).value).toEqual( - put(showNotification('warning', 'Your addresses nonce could not be fetched')) - ); - expect(gens.noWallet.next().value).toEqual(put(getNonceFailed())); - expect(gens.noWallet.next().done).toEqual(true); + gens.noWallet.next(null); // No wallet inst + expect(gens.noWallet.next(offline).done).toEqual(true); }); it('should select getOffline', () => {