From 7acaae37a969339d1b4971f80d7fd842266bb60b Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Thu, 8 Nov 2018 14:57:39 -0800 Subject: feat(instant): Heartbeat for updating account info --- .../src/components/zero_ex_instant_provider.tsx | 13 +++++- packages/instant/src/constants.ts | 1 + packages/instant/src/redux/async_data.ts | 4 +- packages/instant/src/util/hearbeats.ts | 47 ++++++++++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 packages/instant/src/util/hearbeats.ts (limited to 'packages/instant') diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index fa0588b71..02f14c5b6 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -5,6 +5,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { Provider as ReduxProvider } from 'react-redux'; +import { ACCOUNT_UPDATE_INTERVAL_TIME_MS } from '../constants'; import { SelectedAssetThemeProvider } from '../containers/selected_asset_theme_provider'; import { asyncData } from '../redux/async_data'; import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer'; @@ -14,6 +15,7 @@ import { AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; import { gasPriceEstimator } from '../util/gas_price_estimator'; +import { AccountUpdateHeartbeat } from '../util/hearbeats'; import { providerStateFactory } from '../util/provider_state_factory'; fonts.include(); @@ -37,6 +39,7 @@ export interface ZeroExInstantProviderOptionalProps { export class ZeroExInstantProvider extends React.Component { private readonly _store: Store; + private _accountUpdateHeartbeat?: AccountUpdateHeartbeat; // TODO(fragosti): Write tests for this beast once we inject a provider. private static _mergeDefaultStateWithProps( props: ZeroExInstantProviderProps, @@ -93,7 +96,10 @@ export class ZeroExInstantProvider extends React.Component diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index 705313ce7..d0a86c780 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -10,6 +10,7 @@ export const WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX = 'Transaction fa export const GWEI_IN_WEI = new BigNumber(1000000000); export const ONE_SECOND_MS = 1000; export const ONE_MINUTE_MS = ONE_SECOND_MS * 60; +export const ACCOUNT_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 15; export const DEFAULT_GAS_PRICE = GWEI_IN_WEI.mul(6); export const DEFAULT_ESTIMATED_TRANSACTION_TIME_MS = ONE_MINUTE_MS * 2; export const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info'; diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index 862f60e78..055d3de20 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -34,10 +34,10 @@ export const asyncData = { store.dispatch(actions.setAvailableAssets([])); } }, - fetchAccountInfoAndDispatchToStore: async (store: Store) => { + fetchAccountInfoAndDispatchToStore: async (store: Store, options = { setLoading: true }) => { const { providerState } = store.getState(); const web3Wrapper = providerState.web3Wrapper; - if (providerState.account.state !== AccountState.Loading) { + if (options.setLoading && providerState.account.state !== AccountState.Loading) { store.dispatch(actions.setAccountStateLoading()); } let availableAddresses: string[]; diff --git a/packages/instant/src/util/hearbeats.ts b/packages/instant/src/util/hearbeats.ts new file mode 100644 index 000000000..19b448bef --- /dev/null +++ b/packages/instant/src/util/hearbeats.ts @@ -0,0 +1,47 @@ +import * as _ from 'lodash'; + +import { asyncData } from './../redux/async_data'; +import { Store } from './../redux/store'; + +export class AccountUpdateHeartbeat { + private _intervalId?: number; + private _pendingRequest?: boolean; + private _store?: Store; + + public start(store: Store, intervalTimeMs: number): void { + if (!_.isUndefined(this._intervalId)) { + throw new Error('Heartbeat is running, please stop before restarting'); + } + this._store = store; + // Kick off initial first request + this._performActionAsync(true); + // Set interval for heartbeat + this._intervalId = window.setInterval(this._performActionAsync.bind(this, false), intervalTimeMs); + } + + public stop(): void { + if (!_.isUndefined(this._intervalId)) { + window.clearInterval(this._intervalId); + this._resetState(); + } + } + + private _resetState(): void { + this._intervalId = undefined; + this._pendingRequest = false; + this._store = undefined; + } + + private async _performActionAsync(setLoading: boolean): Promise { + if (this._pendingRequest || _.isUndefined(this._store)) { + return; + } + + this._pendingRequest = true; + try { + await asyncData.fetchAccountInfoAndDispatchToStore(this._store, { setLoading }); + } finally { + this._pendingRequest = false; + } + } +} -- cgit v1.2.3 From 624f5cee8dd4a21e7becd5664f47f5780ade7e69 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Thu, 8 Nov 2018 15:01:59 -0800 Subject: linting --- packages/instant/src/util/hearbeats.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'packages/instant') diff --git a/packages/instant/src/util/hearbeats.ts b/packages/instant/src/util/hearbeats.ts index 19b448bef..78ab86360 100644 --- a/packages/instant/src/util/hearbeats.ts +++ b/packages/instant/src/util/hearbeats.ts @@ -14,6 +14,7 @@ export class AccountUpdateHeartbeat { } this._store = store; // Kick off initial first request + // tslint:disable-next-line:no-floating-promises this._performActionAsync(true); // Set interval for heartbeat this._intervalId = window.setInterval(this._performActionAsync.bind(this, false), intervalTimeMs); -- cgit v1.2.3 From dd4d3b10cf8c0bb08f981128232d3663d01806eb Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Thu, 8 Nov 2018 16:14:00 -0800 Subject: wip: abstract out updating buy quote --- .../selected_erc20_asset_amount_input.ts | 43 ++--------------- packages/instant/src/util/buy_quote_fetcher.ts | 55 ++++++++++++++++++++++ 2 files changed, 59 insertions(+), 39 deletions(-) create mode 100644 packages/instant/src/util/buy_quote_fetcher.ts (limited to 'packages/instant') diff --git a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts index 784eb4bd0..97fb073aa 100644 --- a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts +++ b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts @@ -1,20 +1,17 @@ -import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer'; +import { AssetBuyer } from '@0x/asset-buyer'; import { AssetProxyId } from '@0x/types'; import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; import * as _ from 'lodash'; import * as React from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; -import { oc } from 'ts-optchain'; import { ERC20AssetAmountInput } from '../components/erc20_asset_amount_input'; import { Action, actions } from '../redux/actions'; import { State } from '../redux/reducer'; import { ColorOption } from '../style/theme'; import { AffiliateInfo, ERC20Asset, OrderProcessState } from '../types'; -import { assetUtils } from '../util/asset'; -import { errorFlasher } from '../util/error_flasher'; +import { updateBuyQuoteOrFlashErrorAsync } from '../util/buy_quote_fetcher'; export interface SelectedERC20AssetAmountInputProps { fontColor?: ColorOption; @@ -77,42 +74,10 @@ const updateBuyQuoteAsync = async ( assetAmount: BigNumber, affiliateInfo?: AffiliateInfo, ): Promise => { - // get a new buy quote. - const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, asset.metaData.decimals); - // mark quote as pending dispatch(actions.setQuoteRequestStatePending()); - - const feePercentage = oc(affiliateInfo).feePercentage(); - let newBuyQuote: BuyQuote | undefined; - try { - newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue, { feePercentage }); - } catch (error) { - dispatch(actions.setQuoteRequestStateFailure()); - let errorMessage; - if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { - const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); - errorMessage = `Not enough ${assetName} available`; - } else if (error.message === AssetBuyerError.InsufficientZrxLiquidity) { - errorMessage = 'Not enough ZRX available'; - } else if ( - error.message === AssetBuyerError.StandardRelayerApiError || - error.message.startsWith(AssetBuyerError.AssetUnavailable) - ) { - const assetName = assetUtils.bestNameForAsset(asset, 'This asset'); - errorMessage = `${assetName} is currently unavailable`; - } - if (!_.isUndefined(errorMessage)) { - errorFlasher.flashNewErrorMessage(dispatch, errorMessage); - } else { - throw error; - } - return; - } - // We have a successful new buy quote - errorFlasher.clearError(dispatch); - // invalidate the last buy quote. - dispatch(actions.updateLatestBuyQuote(newBuyQuote)); + // kick of buy quote + updateBuyQuoteOrFlashErrorAsync(assetBuyer, asset, assetAmount, dispatch, affiliateInfo); }; const debouncedUpdateBuyQuoteAsync = _.debounce(updateBuyQuoteAsync, 200, { trailing: true }); diff --git a/packages/instant/src/util/buy_quote_fetcher.ts b/packages/instant/src/util/buy_quote_fetcher.ts new file mode 100644 index 000000000..c3e65cbc3 --- /dev/null +++ b/packages/instant/src/util/buy_quote_fetcher.ts @@ -0,0 +1,55 @@ +// TODO: rename file and export object +import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as _ from 'lodash'; +import { Dispatch } from 'redux'; +import { oc } from 'ts-optchain'; + +import { Action, actions } from '../redux/actions'; +import { AffiliateInfo, ERC20Asset } from '../types'; +import { assetUtils } from '../util/asset'; + +import { errorFlasher } from './error_flasher'; + +export const updateBuyQuoteOrFlashErrorAsync = async ( + assetBuyer: AssetBuyer, + asset: ERC20Asset, + assetAmount: BigNumber, + dispatch: Dispatch, + affiliateInfo?: AffiliateInfo, +) => { + // get a new buy quote. + const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, asset.metaData.decimals); + + const feePercentage = oc(affiliateInfo).feePercentage(); + let newBuyQuote: BuyQuote | undefined; + try { + newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue, { feePercentage }); + } catch (error) { + dispatch(actions.setQuoteRequestStateFailure()); + let errorMessage; + if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { + const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); + errorMessage = `Not enough ${assetName} available`; + } else if (error.message === AssetBuyerError.InsufficientZrxLiquidity) { + errorMessage = 'Not enough ZRX available'; + } else if ( + error.message === AssetBuyerError.StandardRelayerApiError || + error.message.startsWith(AssetBuyerError.AssetUnavailable) + ) { + const assetName = assetUtils.bestNameForAsset(asset, 'This asset'); + errorMessage = `${assetName} is currently unavailable`; + } + if (!_.isUndefined(errorMessage)) { + errorFlasher.flashNewErrorMessage(dispatch, errorMessage); + } else { + throw error; + } + return; + } + // We have a successful new buy quote + errorFlasher.clearError(dispatch); + // invalidate the last buy quote. + dispatch(actions.updateLatestBuyQuote(newBuyQuote)); +}; -- cgit v1.2.3 From e45b6c7e98a33de0e13f4ab7db8b630900dbb960 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Thu, 8 Nov 2018 16:54:45 -0800 Subject: Make heartbeat more generic --- .../src/components/zero_ex_instant_provider.tsx | 9 +++-- packages/instant/src/util/hearbeats.ts | 43 ++++++++++++---------- 2 files changed, 29 insertions(+), 23 deletions(-) (limited to 'packages/instant') diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 02f14c5b6..1805a11af 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -15,7 +15,7 @@ import { AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; import { gasPriceEstimator } from '../util/gas_price_estimator'; -import { AccountUpdateHeartbeat } from '../util/hearbeats'; +import { generateAccountHeartbeater, Heartbeater } from '../util/hearbeats'; import { providerStateFactory } from '../util/provider_state_factory'; fonts.include(); @@ -39,7 +39,7 @@ export interface ZeroExInstantProviderOptionalProps { export class ZeroExInstantProvider extends React.Component { private readonly _store: Store; - private _accountUpdateHeartbeat?: AccountUpdateHeartbeat; + private _accountUpdateHeartbeat?: Heartbeater; // TODO(fragosti): Write tests for this beast once we inject a provider. private static _mergeDefaultStateWithProps( props: ZeroExInstantProviderProps, @@ -97,8 +97,9 @@ export class ZeroExInstantProvider extends React.Component Promise; +export class Heartbeater { private _intervalId?: number; - private _pendingRequest?: boolean; - private _store?: Store; + private _pendingRequest: boolean; + private _performingFunctionAsync: HeartbeatableFunction; + + public constructor(_performingFunctionAsync: HeartbeatableFunction) { + this._performingFunctionAsync = _performingFunctionAsync; + this._pendingRequest = false; + } - public start(store: Store, intervalTimeMs: number): void { + public start(intervalTimeMs: number): void { if (!_.isUndefined(this._intervalId)) { throw new Error('Heartbeat is running, please stop before restarting'); } - this._store = store; - // Kick off initial first request - // tslint:disable-next-line:no-floating-promises - this._performActionAsync(true); - // Set interval for heartbeat - this._intervalId = window.setInterval(this._performActionAsync.bind(this, false), intervalTimeMs); + this._trackAndPerformAsync(); + this._intervalId = window.setInterval(this._trackAndPerformAsync.bind(this), intervalTimeMs); } public stop(): void { - if (!_.isUndefined(this._intervalId)) { + if (this._intervalId) { window.clearInterval(this._intervalId); - this._resetState(); } - } - - private _resetState(): void { this._intervalId = undefined; this._pendingRequest = false; - this._store = undefined; } - private async _performActionAsync(setLoading: boolean): Promise { - if (this._pendingRequest || _.isUndefined(this._store)) { + private async _trackAndPerformAsync(): Promise { + if (this._pendingRequest) { return; } this._pendingRequest = true; try { - await asyncData.fetchAccountInfoAndDispatchToStore(this._store, { setLoading }); + this._performingFunctionAsync(); } finally { this._pendingRequest = false; } } } + +export const generateAccountHeartbeater = (store: Store): Heartbeater => { + return new Heartbeater(async () => { + await asyncData.fetchAccountInfoAndDispatchToStore(store, { setLoading: false }); + }); +}; -- cgit v1.2.3 From 1e39d56cf751fd922d6fec86ecc8bc70b20bc6bb Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Thu, 8 Nov 2018 17:09:26 -0800 Subject: wip: BuyQuote heartbeat --- .../src/components/zero_ex_instant_provider.tsx | 13 +++++++++++-- packages/instant/src/constants.ts | 1 + packages/instant/src/util/buy_quote_fetcher.ts | 18 ++++++++++++++++++ packages/instant/src/util/hearbeats.ts | 18 +++++++++++++++--- 4 files changed, 45 insertions(+), 5 deletions(-) (limited to 'packages/instant') diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 1805a11af..24ae0c4b6 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -5,7 +5,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { Provider as ReduxProvider } from 'react-redux'; -import { ACCOUNT_UPDATE_INTERVAL_TIME_MS } from '../constants'; +import { ACCOUNT_UPDATE_INTERVAL_TIME_MS, BUY_QUOTE_UPDATE_INTERVAL_TIME_MS } from '../constants'; import { SelectedAssetThemeProvider } from '../containers/selected_asset_theme_provider'; import { asyncData } from '../redux/async_data'; import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer'; @@ -15,7 +15,7 @@ import { AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; import { gasPriceEstimator } from '../util/gas_price_estimator'; -import { generateAccountHeartbeater, Heartbeater } from '../util/hearbeats'; +import { generateAccountHeartbeater, Heartbeater, generateBuyQuoteHeartbeater } from '../util/hearbeats'; import { providerStateFactory } from '../util/provider_state_factory'; fonts.include(); @@ -40,6 +40,8 @@ export interface ZeroExInstantProviderOptionalProps { export class ZeroExInstantProvider extends React.Component { private readonly _store: Store; private _accountUpdateHeartbeat?: Heartbeater; + private _buyQuoteHeartbeat?: Heartbeater; + // TODO(fragosti): Write tests for this beast once we inject a provider. private static _mergeDefaultStateWithProps( props: ZeroExInstantProviderProps, @@ -101,6 +103,10 @@ export class ZeroExInstantProvider extends React.Component) => { + const { selectedAsset, selectedAssetAmount, affiliateInfo } = state; + const assetBuyer = state.providerState.assetBuyer; + + if (selectedAsset && selectedAssetAmount && selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20) { + // TODO: maybe dont do in the case of an error showing + updateBuyQuoteOrFlashErrorAsync( + assetBuyer, + selectedAsset as ERC20Asset, // TODO: better way to do this? + selectedAssetAmount, + dispatch, + affiliateInfo, + ); + } +}; diff --git a/packages/instant/src/util/hearbeats.ts b/packages/instant/src/util/hearbeats.ts index 443fd13ec..ecd7c5068 100644 --- a/packages/instant/src/util/hearbeats.ts +++ b/packages/instant/src/util/hearbeats.ts @@ -1,9 +1,14 @@ // TODO: rename file import * as _ from 'lodash'; +import { Dispatch } from 'redux'; -import { asyncData } from './../redux/async_data'; -import { Store } from './../redux/store'; +import { Action } from '../redux/actions'; +import { asyncData } from '../redux/async_data'; +import { State } from '../redux/reducer'; +import { Store } from '../redux/store'; + +import { updateBuyQuoteOrFlashErrorAsyncForState } from './buy_quote_fetcher'; type HeartbeatableFunction = () => Promise; export class Heartbeater { @@ -39,7 +44,7 @@ export class Heartbeater { this._pendingRequest = true; try { - this._performingFunctionAsync(); + await this._performingFunctionAsync(); } finally { this._pendingRequest = false; } @@ -51,3 +56,10 @@ export const generateAccountHeartbeater = (store: Store): Heartbeater => { await asyncData.fetchAccountInfoAndDispatchToStore(store, { setLoading: false }); }); }; + +export const generateBuyQuoteHeartbeater = (store: Store): Heartbeater => { + return new Heartbeater(async () => { + await updateBuyQuoteOrFlashErrorAsyncForState(store.getState(), store.dispatch); + return Promise.resolve(); + }); +}; -- cgit v1.2.3 From 297a62fe80142897250f6dd6fddb4cdf1d3fe3ee Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Thu, 8 Nov 2018 17:13:22 -0800 Subject: move files around and rename --- .../src/components/zero_ex_instant_provider.tsx | 3 +- packages/instant/src/util/hearbeats.ts | 65 ---------------------- packages/instant/src/util/heartbeater.ts | 42 ++++++++++++++ packages/instant/src/util/heartbeater_factory.ts | 18 ++++++ 4 files changed, 62 insertions(+), 66 deletions(-) delete mode 100644 packages/instant/src/util/hearbeats.ts create mode 100644 packages/instant/src/util/heartbeater.ts create mode 100644 packages/instant/src/util/heartbeater_factory.ts (limited to 'packages/instant') diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 24ae0c4b6..bbc20dab6 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -15,7 +15,8 @@ import { AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; import { gasPriceEstimator } from '../util/gas_price_estimator'; -import { generateAccountHeartbeater, Heartbeater, generateBuyQuoteHeartbeater } from '../util/hearbeats'; +import { Heartbeater } from '../util/heartbeater'; +import { generateAccountHeartbeater, generateBuyQuoteHeartbeater } from '../util/heartbeater_factory'; import { providerStateFactory } from '../util/provider_state_factory'; fonts.include(); diff --git a/packages/instant/src/util/hearbeats.ts b/packages/instant/src/util/hearbeats.ts deleted file mode 100644 index ecd7c5068..000000000 --- a/packages/instant/src/util/hearbeats.ts +++ /dev/null @@ -1,65 +0,0 @@ -// TODO: rename file - -import * as _ from 'lodash'; -import { Dispatch } from 'redux'; - -import { Action } from '../redux/actions'; -import { asyncData } from '../redux/async_data'; -import { State } from '../redux/reducer'; -import { Store } from '../redux/store'; - -import { updateBuyQuoteOrFlashErrorAsyncForState } from './buy_quote_fetcher'; - -type HeartbeatableFunction = () => Promise; -export class Heartbeater { - private _intervalId?: number; - private _pendingRequest: boolean; - private _performingFunctionAsync: HeartbeatableFunction; - - public constructor(_performingFunctionAsync: HeartbeatableFunction) { - this._performingFunctionAsync = _performingFunctionAsync; - this._pendingRequest = false; - } - - public start(intervalTimeMs: number): void { - if (!_.isUndefined(this._intervalId)) { - throw new Error('Heartbeat is running, please stop before restarting'); - } - this._trackAndPerformAsync(); - this._intervalId = window.setInterval(this._trackAndPerformAsync.bind(this), intervalTimeMs); - } - - public stop(): void { - if (this._intervalId) { - window.clearInterval(this._intervalId); - } - this._intervalId = undefined; - this._pendingRequest = false; - } - - private async _trackAndPerformAsync(): Promise { - if (this._pendingRequest) { - return; - } - - this._pendingRequest = true; - try { - await this._performingFunctionAsync(); - } finally { - this._pendingRequest = false; - } - } -} - -export const generateAccountHeartbeater = (store: Store): Heartbeater => { - return new Heartbeater(async () => { - await asyncData.fetchAccountInfoAndDispatchToStore(store, { setLoading: false }); - }); -}; - -export const generateBuyQuoteHeartbeater = (store: Store): Heartbeater => { - return new Heartbeater(async () => { - await updateBuyQuoteOrFlashErrorAsyncForState(store.getState(), store.dispatch); - return Promise.resolve(); - }); -}; diff --git a/packages/instant/src/util/heartbeater.ts b/packages/instant/src/util/heartbeater.ts new file mode 100644 index 000000000..c5a823953 --- /dev/null +++ b/packages/instant/src/util/heartbeater.ts @@ -0,0 +1,42 @@ +import * as _ from 'lodash'; + +type HeartbeatableFunction = () => Promise; +export class Heartbeater { + private _intervalId?: number; + private _pendingRequest: boolean; + private _performingFunctionAsync: HeartbeatableFunction; + + public constructor(_performingFunctionAsync: HeartbeatableFunction) { + this._performingFunctionAsync = _performingFunctionAsync; + this._pendingRequest = false; + } + + public start(intervalTimeMs: number): void { + if (!_.isUndefined(this._intervalId)) { + throw new Error('Heartbeat is running, please stop before restarting'); + } + this._trackAndPerformAsync(); + this._intervalId = window.setInterval(this._trackAndPerformAsync.bind(this), intervalTimeMs); + } + + public stop(): void { + if (this._intervalId) { + window.clearInterval(this._intervalId); + } + this._intervalId = undefined; + this._pendingRequest = false; + } + + private async _trackAndPerformAsync(): Promise { + if (this._pendingRequest) { + return; + } + + this._pendingRequest = true; + try { + await this._performingFunctionAsync(); + } finally { + this._pendingRequest = false; + } + } +} diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts new file mode 100644 index 000000000..fc33787ee --- /dev/null +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -0,0 +1,18 @@ +import { asyncData } from '../redux/async_data'; +import { Store } from '../redux/store'; + +import { updateBuyQuoteOrFlashErrorAsyncForState } from './buy_quote_fetcher'; +import { Heartbeater } from './heartbeater'; + +export const generateAccountHeartbeater = (store: Store): Heartbeater => { + return new Heartbeater(async () => { + await asyncData.fetchAccountInfoAndDispatchToStore(store, { setLoading: false }); + }); +}; + +export const generateBuyQuoteHeartbeater = (store: Store): Heartbeater => { + return new Heartbeater(async () => { + await updateBuyQuoteOrFlashErrorAsyncForState(store.getState(), store.dispatch); + return Promise.resolve(); + }); +}; -- cgit v1.2.3 From fd12bdbbd5eb52af4758fd3821c6e09617f2ecb3 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Thu, 8 Nov 2018 17:13:33 -0800 Subject: Remove old TODO --- packages/instant/src/components/zero_ex_instant_provider.tsx | 1 - 1 file changed, 1 deletion(-) (limited to 'packages/instant') diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index bbc20dab6..34257d25f 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -104,7 +104,6 @@ export class ZeroExInstantProvider extends React.Component Date: Thu, 8 Nov 2018 17:20:15 -0800 Subject: Remove unneeded Promise.resolve --- packages/instant/src/util/heartbeater_factory.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'packages/instant') diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts index fc33787ee..9fac9cf4c 100644 --- a/packages/instant/src/util/heartbeater_factory.ts +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -13,6 +13,5 @@ export const generateAccountHeartbeater = (store: Store): Heartbeater => { export const generateBuyQuoteHeartbeater = (store: Store): Heartbeater => { return new Heartbeater(async () => { await updateBuyQuoteOrFlashErrorAsyncForState(store.getState(), store.dispatch); - return Promise.resolve(); }); }; -- cgit v1.2.3 From d703c13f8eca7f7139581468e86cf6d2fa067c1e Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Thu, 8 Nov 2018 17:21:17 -0800 Subject: Variable name cleanup --- packages/instant/src/util/heartbeater.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'packages/instant') diff --git a/packages/instant/src/util/heartbeater.ts b/packages/instant/src/util/heartbeater.ts index c5a823953..bb4e99383 100644 --- a/packages/instant/src/util/heartbeater.ts +++ b/packages/instant/src/util/heartbeater.ts @@ -3,12 +3,12 @@ import * as _ from 'lodash'; type HeartbeatableFunction = () => Promise; export class Heartbeater { private _intervalId?: number; - private _pendingRequest: boolean; - private _performingFunctionAsync: HeartbeatableFunction; + private _hasPendingRequest: boolean; + private _performFunction: HeartbeatableFunction; public constructor(_performingFunctionAsync: HeartbeatableFunction) { - this._performingFunctionAsync = _performingFunctionAsync; - this._pendingRequest = false; + this._performFunction = _performingFunctionAsync; + this._hasPendingRequest = false; } public start(intervalTimeMs: number): void { @@ -24,19 +24,19 @@ export class Heartbeater { window.clearInterval(this._intervalId); } this._intervalId = undefined; - this._pendingRequest = false; + this._hasPendingRequest = false; } private async _trackAndPerformAsync(): Promise { - if (this._pendingRequest) { + if (this._hasPendingRequest) { return; } - this._pendingRequest = true; + this._hasPendingRequest = true; try { - await this._performingFunctionAsync(); + await this._performFunction(); } finally { - this._pendingRequest = false; + this._hasPendingRequest = false; } } } -- cgit v1.2.3 From 2e8f74abce7be3a5799bb93a0360d299de55b621 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 9 Nov 2018 10:40:47 -0800 Subject: Use existing functions instead of writing our own --- .../src/components/zero_ex_instant_provider.tsx | 6 +- .../selected_erc20_asset_amount_input.ts | 6 +- packages/instant/src/redux/async_data.ts | 3 +- packages/instant/src/util/buy_quote_fetcher.ts | 75 ---------------------- packages/instant/src/util/buy_quote_updater.ts | 9 ++- packages/instant/src/util/heartbeater.ts | 12 +++- packages/instant/src/util/heartbeater_factory.ts | 11 ++-- 7 files changed, 28 insertions(+), 94 deletions(-) delete mode 100644 packages/instant/src/util/buy_quote_fetcher.ts (limited to 'packages/instant') diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 34257d25f..f5605a187 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -99,13 +99,13 @@ export class ZeroExInstantProvider extends React.Component, @@ -89,7 +87,7 @@ const mapDispatchToProps = ( // even if it's debounced, give them the illusion it's loading dispatch(actions.setQuoteRequestStatePending()); // tslint:disable-next-line:no-floating-promises - debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value, affiliateInfo); + debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value, true, affiliateInfo); } }, }); diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index a50f24cba..7af6e1e56 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -74,7 +74,7 @@ export const asyncData = { return; } }, - fetchCurrentBuyQuoteAndDispatchToStore: async (store: Store) => { + fetchCurrentBuyQuoteAndDispatchToStore: async (store: Store, setPending: boolean) => { const { providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = store.getState(); const assetBuyer = providerState.assetBuyer; if ( @@ -87,6 +87,7 @@ export const asyncData = { store.dispatch, selectedAsset as ERC20Asset, selectedAssetAmount, + setPending, affiliateInfo, ); } diff --git a/packages/instant/src/util/buy_quote_fetcher.ts b/packages/instant/src/util/buy_quote_fetcher.ts deleted file mode 100644 index 22ce835e8..000000000 --- a/packages/instant/src/util/buy_quote_fetcher.ts +++ /dev/null @@ -1,75 +0,0 @@ -// TODO: rename file and export object -// TODO: delete this - -import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer'; -import { AssetProxyId } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as _ from 'lodash'; -import { Dispatch } from 'redux'; -import { oc } from 'ts-optchain'; - -import { Action, actions } from '../redux/actions'; -import { State } from '../redux/reducer'; -import { AffiliateInfo, ERC20Asset } from '../types'; -import { assetUtils } from '../util/asset'; - -import { errorFlasher } from './error_flasher'; - -export const updateBuyQuoteOrFlashErrorAsync = async ( - assetBuyer: AssetBuyer, - asset: ERC20Asset, - assetAmount: BigNumber, - dispatch: Dispatch, - affiliateInfo?: AffiliateInfo, -) => { - // get a new buy quote. - const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, asset.metaData.decimals); - - const feePercentage = oc(affiliateInfo).feePercentage(); - let newBuyQuote: BuyQuote | undefined; - try { - newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue, { feePercentage }); - } catch (error) { - dispatch(actions.setQuoteRequestStateFailure()); - let errorMessage; - if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { - const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); - errorMessage = `Not enough ${assetName} available`; - } else if (error.message === AssetBuyerError.InsufficientZrxLiquidity) { - errorMessage = 'Not enough ZRX available'; - } else if ( - error.message === AssetBuyerError.StandardRelayerApiError || - error.message.startsWith(AssetBuyerError.AssetUnavailable) - ) { - const assetName = assetUtils.bestNameForAsset(asset, 'This asset'); - errorMessage = `${assetName} is currently unavailable`; - } - if (!_.isUndefined(errorMessage)) { - errorFlasher.flashNewErrorMessage(dispatch, errorMessage); - } else { - throw error; - } - return; - } - // We have a successful new buy quote - errorFlasher.clearError(dispatch); - // invalidate the last buy quote. - dispatch(actions.updateLatestBuyQuote(newBuyQuote)); -}; - -export const updateBuyQuoteOrFlashErrorAsyncForState = async (state: State, dispatch: Dispatch) => { - const { selectedAsset, selectedAssetAmount, affiliateInfo } = state; - const assetBuyer = state.providerState.assetBuyer; - - if (selectedAsset && selectedAssetAmount && selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20) { - // TODO: maybe dont do in the case of an error showing - updateBuyQuoteOrFlashErrorAsync( - assetBuyer, - selectedAsset as ERC20Asset, // TODO: better way to do this? - selectedAssetAmount, - dispatch, - affiliateInfo, - ); - } -}; diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index e697d3ef7..fb5da311d 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -1,4 +1,5 @@ import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer'; +import { AssetProxyId } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as _ from 'lodash'; @@ -6,6 +7,7 @@ import { Dispatch } from 'redux'; import { oc } from 'ts-optchain'; import { Action, actions } from '../redux/actions'; +import { State } from '../redux/reducer'; import { AffiliateInfo, ERC20Asset } from '../types'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; @@ -16,12 +18,15 @@ export const buyQuoteUpdater = { dispatch: Dispatch, asset: ERC20Asset, assetAmount: BigNumber, + setPending = true, affiliateInfo?: AffiliateInfo, ): Promise => { // get a new buy quote. const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, asset.metaData.decimals); - // mark quote as pending - dispatch(actions.setQuoteRequestStatePending()); + if (setPending) { + // mark quote as pending + dispatch(actions.setQuoteRequestStatePending()); + } const feePercentage = oc(affiliateInfo).feePercentage(); let newBuyQuote: BuyQuote | undefined; try { diff --git a/packages/instant/src/util/heartbeater.ts b/packages/instant/src/util/heartbeater.ts index bb4e99383..87af48423 100644 --- a/packages/instant/src/util/heartbeater.ts +++ b/packages/instant/src/util/heartbeater.ts @@ -4,18 +4,24 @@ type HeartbeatableFunction = () => Promise; export class Heartbeater { private _intervalId?: number; private _hasPendingRequest: boolean; + private _performImmediatelyOnStart: boolean; private _performFunction: HeartbeatableFunction; - public constructor(_performingFunctionAsync: HeartbeatableFunction) { - this._performFunction = _performingFunctionAsync; + public constructor(performingFunctionAsync: HeartbeatableFunction, performImmediatelyOnStart: boolean) { + this._performFunction = performingFunctionAsync; this._hasPendingRequest = false; + this._performImmediatelyOnStart = performImmediatelyOnStart; } public start(intervalTimeMs: number): void { if (!_.isUndefined(this._intervalId)) { throw new Error('Heartbeat is running, please stop before restarting'); } - this._trackAndPerformAsync(); + + if (this._performImmediatelyOnStart) { + this._trackAndPerformAsync(); + } + this._intervalId = window.setInterval(this._trackAndPerformAsync.bind(this), intervalTimeMs); } diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts index 9fac9cf4c..beedb66c8 100644 --- a/packages/instant/src/util/heartbeater_factory.ts +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -1,17 +1,16 @@ import { asyncData } from '../redux/async_data'; import { Store } from '../redux/store'; -import { updateBuyQuoteOrFlashErrorAsyncForState } from './buy_quote_fetcher'; import { Heartbeater } from './heartbeater'; -export const generateAccountHeartbeater = (store: Store): Heartbeater => { +export const generateAccountHeartbeater = (store: Store, performImmediatelyOnStart: boolean): Heartbeater => { return new Heartbeater(async () => { await asyncData.fetchAccountInfoAndDispatchToStore(store, { setLoading: false }); - }); + }, performImmediatelyOnStart); }; -export const generateBuyQuoteHeartbeater = (store: Store): Heartbeater => { +export const generateBuyQuoteHeartbeater = (store: Store, performImmediatelyOnStart: boolean): Heartbeater => { return new Heartbeater(async () => { - await updateBuyQuoteOrFlashErrorAsyncForState(store.getState(), store.dispatch); - }); + await asyncData.fetchCurrentBuyQuoteAndDispatchToStore(store, false); + }, performImmediatelyOnStart); }; -- cgit v1.2.3 From 39657b633bd386526f5772238cbebfb976427c07 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 9 Nov 2018 11:04:44 -0800 Subject: Make sure we only update price when they are not in the middle of an order --- packages/instant/src/redux/async_data.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'packages/instant') diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index 7af6e1e56..c7fe4cd0e 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -2,7 +2,7 @@ import { AssetProxyId } from '@0x/types'; import * as _ from 'lodash'; import { BIG_NUMBER_ZERO } from '../constants'; -import { AccountState, ERC20Asset } from '../types'; +import { AccountState, ERC20Asset, OrderProcessState } from '../types'; import { assetUtils } from '../util/asset'; import { buyQuoteUpdater } from '../util/buy_quote_updater'; import { coinbaseApi } from '../util/coinbase_api'; @@ -75,11 +75,12 @@ export const asyncData = { } }, fetchCurrentBuyQuoteAndDispatchToStore: async (store: Store, setPending: boolean) => { - const { providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = store.getState(); + const { buyOrderState, providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = store.getState(); const assetBuyer = providerState.assetBuyer; if ( !_.isUndefined(selectedAssetAmount) && !_.isUndefined(selectedAsset) && + buyOrderState.processState === OrderProcessState.None && selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20 ) { await buyQuoteUpdater.updateBuyQuoteAsync( -- cgit v1.2.3 From 474db7c18de63429f72511796291ff135c77f10b Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 9 Nov 2018 11:22:46 -0800 Subject: Emulate named parameters with interface --- packages/instant/src/components/zero_ex_instant_provider.tsx | 9 ++++++--- packages/instant/src/redux/async_data.ts | 3 ++- packages/instant/src/util/heartbeater_factory.ts | 12 +++++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) (limited to 'packages/instant') diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index f5605a187..20d677dc2 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -100,12 +100,15 @@ export class ZeroExInstantProvider extends React.Component { + fetchCurrentBuyQuoteAndDispatchToStore: async (options: { store: Store; setPending: boolean }) => { + const { store, setPending } = options; const { buyOrderState, providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = store.getState(); const assetBuyer = providerState.assetBuyer; if ( diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts index beedb66c8..0feb05422 100644 --- a/packages/instant/src/util/heartbeater_factory.ts +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -3,14 +3,20 @@ import { Store } from '../redux/store'; import { Heartbeater } from './heartbeater'; -export const generateAccountHeartbeater = (store: Store, performImmediatelyOnStart: boolean): Heartbeater => { +export interface HeartbeatFactoryOptions { + store: Store; + performImmediatelyOnStart: boolean; +} +export const generateAccountHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => { + const { store, performImmediatelyOnStart } = options; return new Heartbeater(async () => { await asyncData.fetchAccountInfoAndDispatchToStore(store, { setLoading: false }); }, performImmediatelyOnStart); }; -export const generateBuyQuoteHeartbeater = (store: Store, performImmediatelyOnStart: boolean): Heartbeater => { +export const generateBuyQuoteHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => { + const { store, performImmediatelyOnStart } = options; return new Heartbeater(async () => { - await asyncData.fetchCurrentBuyQuoteAndDispatchToStore(store, false); + await asyncData.fetchCurrentBuyQuoteAndDispatchToStore({ store, setPending: false }); }, performImmediatelyOnStart); }; -- cgit v1.2.3 From 36b8c9c5dd6cc650eaed8a2a6abd8f596c189fed Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 9 Nov 2018 11:28:08 -0800 Subject: Use interface like named parameters here --- packages/instant/src/redux/async_data.ts | 5 +++-- packages/instant/src/util/heartbeater_factory.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'packages/instant') diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index 61de54d82..1f1cafdf3 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -36,10 +36,11 @@ export const asyncData = { store.dispatch(actions.setAvailableAssets([])); } }, - fetchAccountInfoAndDispatchToStore: async (store: Store, options = { setLoading: true }) => { + fetchAccountInfoAndDispatchToStore: async (options: { store: Store; setLoading: boolean }) => { + const { store, setLoading } = options; const { providerState } = store.getState(); const web3Wrapper = providerState.web3Wrapper; - if (options.setLoading && providerState.account.state !== AccountState.Loading) { + if (setLoading && providerState.account.state !== AccountState.Loading) { store.dispatch(actions.setAccountStateLoading()); } let availableAddresses: string[]; diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts index 0feb05422..805483b71 100644 --- a/packages/instant/src/util/heartbeater_factory.ts +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -10,7 +10,7 @@ export interface HeartbeatFactoryOptions { export const generateAccountHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => { const { store, performImmediatelyOnStart } = options; return new Heartbeater(async () => { - await asyncData.fetchAccountInfoAndDispatchToStore(store, { setLoading: false }); + await asyncData.fetchAccountInfoAndDispatchToStore({ store, setLoading: false }); }, performImmediatelyOnStart); }; -- cgit v1.2.3 From cc8debe53b9a6efc75b91f1b6c47f0820c3beb8c Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 9 Nov 2018 11:39:36 -0800 Subject: Linting and renaming variables --- .../instant/src/components/zero_ex_instant_provider.tsx | 11 +++++++---- packages/instant/src/redux/async_data.ts | 12 ++++++------ packages/instant/src/util/buy_quote_updater.ts | 2 -- packages/instant/src/util/heartbeater.ts | 5 +++-- packages/instant/src/util/heartbeater_factory.ts | 14 +++++++------- 5 files changed, 23 insertions(+), 21 deletions(-) (limited to 'packages/instant') diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 20d677dc2..1223b477e 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -98,17 +98,20 @@ export class ZeroExInstantProvider extends React.Component { - const { store, setLoading } = options; + fetchAccountInfoAndDispatchToStore: async (options: { store: Store; shouldSetToLoading: boolean }) => { + const { store, shouldSetToLoading } = options; const { providerState } = store.getState(); const web3Wrapper = providerState.web3Wrapper; - if (setLoading && providerState.account.state !== AccountState.Loading) { + if (shouldSetToLoading && providerState.account.state !== AccountState.Loading) { store.dispatch(actions.setAccountStateLoading()); } let availableAddresses: string[]; @@ -75,8 +75,8 @@ export const asyncData = { return; } }, - fetchCurrentBuyQuoteAndDispatchToStore: async (options: { store: Store; setPending: boolean }) => { - const { store, setPending } = options; + fetchCurrentBuyQuoteAndDispatchToStore: async (options: { store: Store; shouldSetPending: boolean }) => { + const { store, shouldSetPending } = options; const { buyOrderState, providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = store.getState(); const assetBuyer = providerState.assetBuyer; if ( @@ -90,7 +90,7 @@ export const asyncData = { store.dispatch, selectedAsset as ERC20Asset, selectedAssetAmount, - setPending, + shouldSetPending, affiliateInfo, ); } diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index fb5da311d..c33e28f1c 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -1,5 +1,4 @@ import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer'; -import { AssetProxyId } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as _ from 'lodash'; @@ -7,7 +6,6 @@ import { Dispatch } from 'redux'; import { oc } from 'ts-optchain'; import { Action, actions } from '../redux/actions'; -import { State } from '../redux/reducer'; import { AffiliateInfo, ERC20Asset } from '../types'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; diff --git a/packages/instant/src/util/heartbeater.ts b/packages/instant/src/util/heartbeater.ts index 87af48423..a5b42d87f 100644 --- a/packages/instant/src/util/heartbeater.ts +++ b/packages/instant/src/util/heartbeater.ts @@ -4,8 +4,8 @@ type HeartbeatableFunction = () => Promise; export class Heartbeater { private _intervalId?: number; private _hasPendingRequest: boolean; - private _performImmediatelyOnStart: boolean; - private _performFunction: HeartbeatableFunction; + private readonly _performImmediatelyOnStart: boolean; + private readonly _performFunction: HeartbeatableFunction; public constructor(performingFunctionAsync: HeartbeatableFunction, performImmediatelyOnStart: boolean) { this._performFunction = performingFunctionAsync; @@ -19,6 +19,7 @@ export class Heartbeater { } if (this._performImmediatelyOnStart) { + // tslint:disable-next-line:no-floating-promises this._trackAndPerformAsync(); } diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts index 805483b71..96a8ac4e6 100644 --- a/packages/instant/src/util/heartbeater_factory.ts +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -5,18 +5,18 @@ import { Heartbeater } from './heartbeater'; export interface HeartbeatFactoryOptions { store: Store; - performImmediatelyOnStart: boolean; + shouldPerformImmediatelyOnStart: boolean; } export const generateAccountHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => { - const { store, performImmediatelyOnStart } = options; + const { store, shouldPerformImmediatelyOnStart } = options; return new Heartbeater(async () => { - await asyncData.fetchAccountInfoAndDispatchToStore({ store, setLoading: false }); - }, performImmediatelyOnStart); + await asyncData.fetchAccountInfoAndDispatchToStore({ store, shouldSetToLoading: false }); + }, shouldPerformImmediatelyOnStart); }; export const generateBuyQuoteHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => { - const { store, performImmediatelyOnStart } = options; + const { store, shouldPerformImmediatelyOnStart } = options; return new Heartbeater(async () => { - await asyncData.fetchCurrentBuyQuoteAndDispatchToStore({ store, setPending: false }); - }, performImmediatelyOnStart); + await asyncData.fetchCurrentBuyQuoteAndDispatchToStore({ store, shouldSetPending: false }); + }, shouldPerformImmediatelyOnStart); }; -- cgit v1.2.3 From acb7e876b20f89cf5e1df614cb81de38a66fa6b1 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 9 Nov 2018 15:19:16 -0800 Subject: Update account more frequently --- packages/instant/src/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/instant') diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index 37320e21d..6bfbc690e 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -11,7 +11,7 @@ export const WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX = 'Transaction fa export const GWEI_IN_WEI = new BigNumber(1000000000); export const ONE_SECOND_MS = 1000; export const ONE_MINUTE_MS = ONE_SECOND_MS * 60; -export const ACCOUNT_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 15; +export const ACCOUNT_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 5; export const BUY_QUOTE_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 15; export const DEFAULT_GAS_PRICE = GWEI_IN_WEI.mul(6); export const DEFAULT_ESTIMATED_TRANSACTION_TIME_MS = ONE_MINUTE_MS * 2; -- cgit v1.2.3 From 5c1b1a120362c1e84746d8a6695fe4de0ae6fb89 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 9 Nov 2018 15:28:08 -0800 Subject: Using built in intervalUtils instead of rolling own --- packages/instant/src/util/heartbeater.ts | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) (limited to 'packages/instant') diff --git a/packages/instant/src/util/heartbeater.ts b/packages/instant/src/util/heartbeater.ts index a5b42d87f..dbc72da6a 100644 --- a/packages/instant/src/util/heartbeater.ts +++ b/packages/instant/src/util/heartbeater.ts @@ -1,15 +1,14 @@ +import { intervalUtils } from '@0x/utils'; import * as _ from 'lodash'; type HeartbeatableFunction = () => Promise; export class Heartbeater { - private _intervalId?: number; - private _hasPendingRequest: boolean; + private _intervalId?: NodeJS.Timer; private readonly _performImmediatelyOnStart: boolean; private readonly _performFunction: HeartbeatableFunction; public constructor(performingFunctionAsync: HeartbeatableFunction, performImmediatelyOnStart: boolean) { this._performFunction = performingFunctionAsync; - this._hasPendingRequest = false; this._performImmediatelyOnStart = performImmediatelyOnStart; } @@ -20,30 +19,16 @@ export class Heartbeater { if (this._performImmediatelyOnStart) { // tslint:disable-next-line:no-floating-promises - this._trackAndPerformAsync(); + this._performFunction(); } - this._intervalId = window.setInterval(this._trackAndPerformAsync.bind(this), intervalTimeMs); + this._intervalId = intervalUtils.setAsyncExcludingInterval(this._performFunction, intervalTimeMs, () => {}); } public stop(): void { if (this._intervalId) { - window.clearInterval(this._intervalId); + intervalUtils.clearInterval(this._intervalId); } this._intervalId = undefined; - this._hasPendingRequest = false; - } - - private async _trackAndPerformAsync(): Promise { - if (this._hasPendingRequest) { - return; - } - - this._hasPendingRequest = true; - try { - await this._performFunction(); - } finally { - this._hasPendingRequest = false; - } } } -- cgit v1.2.3 From 93054ae52b782a7085c482e2510e77ae8bb03520 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 9 Nov 2018 15:28:29 -0800 Subject: Lodash noop --- packages/instant/src/util/heartbeater.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/instant') diff --git a/packages/instant/src/util/heartbeater.ts b/packages/instant/src/util/heartbeater.ts index dbc72da6a..717098635 100644 --- a/packages/instant/src/util/heartbeater.ts +++ b/packages/instant/src/util/heartbeater.ts @@ -22,7 +22,7 @@ export class Heartbeater { this._performFunction(); } - this._intervalId = intervalUtils.setAsyncExcludingInterval(this._performFunction, intervalTimeMs, () => {}); + this._intervalId = intervalUtils.setAsyncExcludingInterval(this._performFunction, intervalTimeMs, _.noop); } public stop(): void { -- cgit v1.2.3 From 4072076965fba42ee02b689db1e97d9a970620f4 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 9 Nov 2018 15:40:13 -0800 Subject: Don't start heartbeat if no account --- .../instant/src/components/zero_ex_instant_provider.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'packages/instant') diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 1223b477e..fbe0e977f 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -11,7 +11,7 @@ import { asyncData } from '../redux/async_data'; import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer'; import { store, Store } from '../redux/store'; import { fonts } from '../style/fonts'; -import { AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types'; +import { AffiliateInfo, AssetMetaData, Network, OrderSource, AccountState } from '../types'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; import { gasPriceEstimator } from '../util/gas_price_estimator'; @@ -99,11 +99,13 @@ export class ZeroExInstantProvider extends React.Component Date: Fri, 9 Nov 2018 15:52:25 -0800 Subject: linting --- packages/instant/src/components/zero_ex_instant_provider.tsx | 2 +- packages/instant/src/util/heartbeater.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'packages/instant') diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index fbe0e977f..11f867abc 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -11,7 +11,7 @@ import { asyncData } from '../redux/async_data'; import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer'; import { store, Store } from '../redux/store'; import { fonts } from '../style/fonts'; -import { AffiliateInfo, AssetMetaData, Network, OrderSource, AccountState } from '../types'; +import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; import { gasPriceEstimator } from '../util/gas_price_estimator'; diff --git a/packages/instant/src/util/heartbeater.ts b/packages/instant/src/util/heartbeater.ts index 717098635..e700d489e 100644 --- a/packages/instant/src/util/heartbeater.ts +++ b/packages/instant/src/util/heartbeater.ts @@ -22,6 +22,7 @@ export class Heartbeater { this._performFunction(); } + // tslint:disable-next-line:no-unbound-method this._intervalId = intervalUtils.setAsyncExcludingInterval(this._performFunction, intervalTimeMs, _.noop); } -- cgit v1.2.3