From 79f0324abc6e59d5595770e44d620e6d0cccd6bc Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 12 Nov 2018 13:30:47 -0800 Subject: feat: integrate wallet flow with heartbeat and other branches --- .../instant/src/components/buy_order_progress.tsx | 2 +- packages/instant/src/components/payment_method.tsx | 12 ++-- .../src/components/zero_ex_instant_provider.tsx | 7 ++- .../containers/connected_account_payment_method.ts | 39 +++++++++++-- packages/instant/src/redux/async_data.ts | 65 ++++++++++++---------- packages/instant/src/util/heartbeater_factory.ts | 4 +- 6 files changed, 81 insertions(+), 48 deletions(-) diff --git a/packages/instant/src/components/buy_order_progress.tsx b/packages/instant/src/components/buy_order_progress.tsx index cb02c8d8c..d93e74c9f 100644 --- a/packages/instant/src/components/buy_order_progress.tsx +++ b/packages/instant/src/components/buy_order_progress.tsx @@ -1,7 +1,7 @@ import * as _ from 'lodash'; import * as React from 'react'; -import { ProgressBar, TimedProgressBar } from '../components/timed_progress_bar'; +import { TimedProgressBar } from '../components/timed_progress_bar'; import { TimeCounter } from '../components/time_counter'; import { Container } from '../components/ui/container'; diff --git a/packages/instant/src/components/payment_method.tsx b/packages/instant/src/components/payment_method.tsx index f596c5ad6..25c879519 100644 --- a/packages/instant/src/components/payment_method.tsx +++ b/packages/instant/src/components/payment_method.tsx @@ -2,7 +2,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { ColorOption } from '../style/theme'; -import { Account, AccountState, Network, StandardSlidingPanelContent } from '../types'; +import { Account, AccountState, Network } from '../types'; import { MetaMaskLogo } from './meta_mask_logo'; import { PaymentMethodDropdown } from './payment_method_dropdown'; @@ -15,7 +15,8 @@ import { Text } from './ui/text'; export interface PaymentMethodProps { account: Account; network: Network; - openStandardSlidingPanel: (content: StandardSlidingPanelContent) => void; + onInstallWalletClick: () => void; + onUnlockWalletClick: () => void; } export class PaymentMethod extends React.Component { @@ -80,7 +81,7 @@ export class PaymentMethod extends React.Component { case AccountState.Locked: return ( } > Please Unlock MetaMask @@ -89,7 +90,7 @@ export class PaymentMethod extends React.Component { case AccountState.None: return ( } > Install MetaMask @@ -107,9 +108,6 @@ export class PaymentMethod extends React.Component { return null; } }; - private readonly _openInstallWalletPanel = () => { - this.props.openStandardSlidingPanel(StandardSlidingPanelContent.InstallWallet); - }; } interface WalletPromptProps { diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 411f118cc..863bc99b7 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -91,12 +91,13 @@ export class ZeroExInstantProvider extends React.Component void; + onInstallWalletClick: () => void; + unlockWalletAndDispatchToStore: (providerState: ProviderState) => void; +} + +interface ConnectedProps { + onInstallWalletClick: () => void; + onUnlockWalletClick: () => void; + account: Account; + network: Network; } +type FinalProps = ConnectedProps & ConnectedAccountPaymentMethodProps; + const mapStateToProps = (state: State, _ownProps: ConnectedAccountPaymentMethodProps): ConnectedState => ({ - account: state.providerState.account, network: state.network, + providerState: state.providerState, }); const mapDispatchToProps = ( dispatch: Dispatch, ownProps: ConnectedAccountPaymentMethodProps, ): ConnectedDispatch => ({ - openStandardSlidingPanel: (content: StandardSlidingPanelContent) => - dispatch(actions.openStandardSlidingPanel(content)), + onInstallWalletClick: () => dispatch(actions.openStandardSlidingPanel(StandardSlidingPanelContent.InstallWallet)), + unlockWalletAndDispatchToStore: async (providerState: ProviderState) => + asyncData.fetchAccountInfoAndDispatchToStore(providerState, dispatch, true), +}); + +const mergeProps = ( + connectedState: ConnectedState, + connectedDispatch: ConnectedDispatch, + ownProps: ConnectedAccountPaymentMethodProps, +): FinalProps => ({ + ...ownProps, + network: connectedState.network, + account: connectedState.providerState.account, + onInstallWalletClick: connectedDispatch.onInstallWalletClick, + onUnlockWalletClick: () => { + connectedDispatch.unlockWalletAndDispatchToStore(connectedState.providerState); + }, }); export const ConnectedAccountPaymentMethod: React.ComponentClass = connect( mapStateToProps, mapDispatchToProps, + mergeProps, )(PaymentMethod); diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index b920ac914..8999ef097 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -1,71 +1,75 @@ import { AssetProxyId } from '@0x/types'; import * as _ from 'lodash'; +import { Dispatch } from 'redux'; import { BIG_NUMBER_ZERO } from '../constants'; -import { AccountState, ERC20Asset, OrderProcessState } from '../types'; +import { AccountState, ERC20Asset, OrderProcessState, ProviderState } from '../types'; import { assetUtils } from '../util/asset'; import { buyQuoteUpdater } from '../util/buy_quote_updater'; import { coinbaseApi } from '../util/coinbase_api'; import { errorFlasher } from '../util/error_flasher'; import { actions } from './actions'; -import { Store } from './store'; +import { State } from './reducer'; export const asyncData = { - fetchEthPriceAndDispatchToStore: async (store: Store) => { + fetchEthPriceAndDispatchToStore: async (dispatch: Dispatch) => { try { const ethUsdPrice = await coinbaseApi.getEthUsdPrice(); - store.dispatch(actions.updateEthUsdPrice(ethUsdPrice)); + dispatch(actions.updateEthUsdPrice(ethUsdPrice)); } catch (e) { const errorMessage = 'Error fetching ETH/USD price'; - errorFlasher.flashNewErrorMessage(store.dispatch, errorMessage); - store.dispatch(actions.updateEthUsdPrice(BIG_NUMBER_ZERO)); + errorFlasher.flashNewErrorMessage(dispatch, errorMessage); + dispatch(actions.updateEthUsdPrice(BIG_NUMBER_ZERO)); } }, - fetchAvailableAssetDatasAndDispatchToStore: async (store: Store) => { - const { providerState, assetMetaDataMap, network } = store.getState(); + fetchAvailableAssetDatasAndDispatchToStore: async (state: State, dispatch: Dispatch) => { + const { providerState, assetMetaDataMap, network } = state; const assetBuyer = providerState.assetBuyer; try { const assetDatas = await assetBuyer.getAvailableAssetDatasAsync(); const assets = assetUtils.createAssetsFromAssetDatas(assetDatas, assetMetaDataMap, network); - store.dispatch(actions.setAvailableAssets(assets)); + dispatch(actions.setAvailableAssets(assets)); } catch (e) { const errorMessage = 'Could not find any assets'; - errorFlasher.flashNewErrorMessage(store.dispatch, errorMessage); + errorFlasher.flashNewErrorMessage(dispatch, errorMessage); // On error, just specify that none are available - store.dispatch(actions.setAvailableAssets([])); + dispatch(actions.setAvailableAssets([])); } }, - fetchAccountInfoAndDispatchToStore: async (options: { store: Store; shouldSetToLoading: boolean }) => { - const { store, shouldSetToLoading } = options; - const { providerState } = store.getState(); + fetchAccountInfoAndDispatchToStore: async ( + providerState: ProviderState, + dispatch: Dispatch, + shouldAttemptUnlock: boolean = false, + shouldSetToLoading: boolean = false, + ) => { const web3Wrapper = providerState.web3Wrapper; const provider = providerState.provider; if (shouldSetToLoading && providerState.account.state !== AccountState.Loading) { - store.dispatch(actions.setAccountStateLoading()); + dispatch(actions.setAccountStateLoading()); } let availableAddresses: string[]; try { // TODO(bmillman): Add support at the web3Wrapper level for calling `eth_requestAccounts` instead of calling enable here const isPrivacyModeEnabled = !_.isUndefined((provider as any).enable); - availableAddresses = isPrivacyModeEnabled - ? await (provider as any).enable() - : await web3Wrapper.getAvailableAddressesAsync(); + availableAddresses = + isPrivacyModeEnabled && shouldAttemptUnlock + ? await (provider as any).enable() + : await web3Wrapper.getAvailableAddressesAsync(); } catch (e) { - store.dispatch(actions.setAccountStateLocked()); + dispatch(actions.setAccountStateLocked()); return; } if (!_.isEmpty(availableAddresses)) { const activeAddress = availableAddresses[0]; - store.dispatch(actions.setAccountStateReady(activeAddress)); + dispatch(actions.setAccountStateReady(activeAddress)); // tslint:disable-next-line:no-floating-promises - asyncData.fetchAccountBalanceAndDispatchToStore(store); + asyncData.fetchAccountBalanceAndDispatchToStore(providerState, dispatch); } else { - store.dispatch(actions.setAccountStateLocked()); + dispatch(actions.setAccountStateLocked()); } }, - fetchAccountBalanceAndDispatchToStore: async (store: Store) => { - const { providerState } = store.getState(); + fetchAccountBalanceAndDispatchToStore: async (providerState: ProviderState, dispatch: Dispatch) => { const web3Wrapper = providerState.web3Wrapper; const account = providerState.account; if (account.state !== AccountState.Ready) { @@ -74,15 +78,18 @@ export const asyncData = { try { const address = account.address; const ethBalanceInWei = await web3Wrapper.getBalanceInWeiAsync(address); - store.dispatch(actions.updateAccountEthBalance({ address, ethBalanceInWei })); + dispatch(actions.updateAccountEthBalance({ address, ethBalanceInWei })); } catch (e) { // leave balance as is return; } }, - fetchCurrentBuyQuoteAndDispatchToStore: async (options: { store: Store; shouldSetPending: boolean }) => { - const { store, shouldSetPending } = options; - const { buyOrderState, providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = store.getState(); + fetchCurrentBuyQuoteAndDispatchToStore: async ( + state: State, + dispatch: Dispatch, + shouldSetPending: boolean = false, + ) => { + const { buyOrderState, providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = state; const assetBuyer = providerState.assetBuyer; if ( !_.isUndefined(selectedAssetAmount) && @@ -92,7 +99,7 @@ export const asyncData = { ) { await buyQuoteUpdater.updateBuyQuoteAsync( assetBuyer, - store.dispatch, + dispatch, selectedAsset as ERC20Asset, selectedAssetAmount, shouldSetPending, diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts index 96a8ac4e6..06fcdb8bb 100644 --- a/packages/instant/src/util/heartbeater_factory.ts +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -10,13 +10,13 @@ export interface HeartbeatFactoryOptions { export const generateAccountHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => { const { store, shouldPerformImmediatelyOnStart } = options; return new Heartbeater(async () => { - await asyncData.fetchAccountInfoAndDispatchToStore({ store, shouldSetToLoading: false }); + await asyncData.fetchAccountInfoAndDispatchToStore(store.getState().providerState, store.dispatch, false); }, shouldPerformImmediatelyOnStart); }; export const generateBuyQuoteHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => { const { store, shouldPerformImmediatelyOnStart } = options; return new Heartbeater(async () => { - await asyncData.fetchCurrentBuyQuoteAndDispatchToStore({ store, shouldSetPending: false }); + await asyncData.fetchCurrentBuyQuoteAndDispatchToStore(store.getState(), store.dispatch, false); }, shouldPerformImmediatelyOnStart); }; -- cgit v1.2.3