From 6cf8d57aee3ed332bd1109f8f39792894147d2dd Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 17 Oct 2018 16:41:35 -0700 Subject: add concept of quoteState --- .../instant/src/components/instant_heading.tsx | 22 ++++++++++++++++---- .../src/containers/selected_asset_amount_input.ts | 24 ++++++++++++++-------- .../containers/selected_asset_instant_heading.ts | 3 +++ packages/instant/src/redux/actions.ts | 4 ++++ packages/instant/src/redux/reducer.ts | 17 ++++++++++++++- packages/instant/src/types.ts | 8 ++++---- 6 files changed, 60 insertions(+), 18 deletions(-) (limited to 'packages/instant') diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx index 492c1b2c0..848056800 100644 --- a/packages/instant/src/components/instant_heading.tsx +++ b/packages/instant/src/components/instant_heading.tsx @@ -4,6 +4,7 @@ import * as React from 'react'; import { SelectedAssetAmountInput } from '../containers/selected_asset_amount_input'; import { ColorOption } from '../style/theme'; +import { AsyncProcessState } from '../types'; import { format } from '../util/format'; import { Container, Flex, Text } from './ui'; @@ -12,20 +13,33 @@ export interface InstantHeadingProps { selectedAssetAmount?: BigNumber; totalEthBaseAmount?: BigNumber; ethUsdPrice?: BigNumber; + quoteState: AsyncProcessState; } const displaytotalEthBaseAmount = ({ selectedAssetAmount, totalEthBaseAmount }: InstantHeadingProps): string => { if (_.isUndefined(selectedAssetAmount)) { return '0 ETH'; } - return format.ethBaseAmount(totalEthBaseAmount, 4, '...loading'); + return format.ethBaseAmount(totalEthBaseAmount, 4, '-'); }; const displayUsdAmount = ({ totalEthBaseAmount, selectedAssetAmount, ethUsdPrice }: InstantHeadingProps): string => { if (_.isUndefined(selectedAssetAmount)) { return '$0.00'; } - return format.ethBaseAmountInUsd(totalEthBaseAmount, ethUsdPrice, 2, '...loading'); + return format.ethBaseAmountInUsd(totalEthBaseAmount, ethUsdPrice, 2, '-'); +}; + +const loadingOrAmount = (quoteState: AsyncProcessState, amount: string): React.ReactNode => { + if (quoteState === AsyncProcessState.PENDING) { + return ( + + …loading + + ); + } else { + return amount; + } }; export const InstantHeading: React.StatelessComponent = props => ( @@ -47,11 +61,11 @@ export const InstantHeading: React.StatelessComponent = pro - {displaytotalEthBaseAmount(props)} + {loadingOrAmount(props.quoteState, displaytotalEthBaseAmount(props))} - {displayUsdAmount(props)} + {loadingOrAmount(props.quoteState, displayUsdAmount(props))} diff --git a/packages/instant/src/containers/selected_asset_amount_input.ts b/packages/instant/src/containers/selected_asset_amount_input.ts index 0d2c6dd7b..87bb0e335 100644 --- a/packages/instant/src/containers/selected_asset_amount_input.ts +++ b/packages/instant/src/containers/selected_asset_amount_input.ts @@ -37,24 +37,25 @@ const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps) const updateBuyQuoteAsync = async ( dispatch: Dispatch, - assetData?: string, - assetAmount?: BigNumber, + assetData: string, + assetAmount: BigNumber, ): Promise => { - if (_.isUndefined(assetAmount) || _.isUndefined(assetData)) { - return; - } // get a new buy quote. const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, zrxDecimals); + // mark quote as pending + dispatch(actions.updateBuyQuoteStatePending()); + let newBuyQuote: BuyQuote | undefined; try { newBuyQuote = await assetBuyer.getBuyQuoteAsync(assetData, baseUnitValue); - errorUtil.errorFlasher.clearError(dispatch); } catch (error) { + dispatch(actions.updateBuyQuoteStateFailure()); errorUtil.errorFlasher.flashNewError(dispatch, error); return; } - + // We have a successful new buy quote + errorUtil.errorFlasher.clearError(dispatch); // invalidate the last buy quote. dispatch(actions.updateLatestBuyQuote(newBuyQuote)); }; @@ -72,8 +73,13 @@ const mapDispatchToProps = ( dispatch(actions.updateLatestBuyQuote(undefined)); // reset our buy state dispatch(actions.updateSelectedAssetBuyState(AsyncProcessState.NONE)); - // tslint:disable-next-line:no-floating-promises - debouncedUpdateBuyQuoteAsync(dispatch, assetData, value); + + if (!_.isUndefined(value) && !_.isUndefined(assetData)) { + // even if it's debounced, give them the illusion it's loading + dispatch(actions.updateBuyQuoteStatePending()); + // tslint:disable-next-line:no-floating-promises + debouncedUpdateBuyQuoteAsync(dispatch, assetData, value); + } }, }); diff --git a/packages/instant/src/containers/selected_asset_instant_heading.ts b/packages/instant/src/containers/selected_asset_instant_heading.ts index c97cfe11a..be31527f1 100644 --- a/packages/instant/src/containers/selected_asset_instant_heading.ts +++ b/packages/instant/src/containers/selected_asset_instant_heading.ts @@ -5,6 +5,7 @@ import { connect } from 'react-redux'; import { oc } from 'ts-optchain'; import { State } from '../redux/reducer'; +import { AsyncProcessState } from '../types'; import { InstantHeading } from '../components/instant_heading'; @@ -14,12 +15,14 @@ interface ConnectedState { selectedAssetAmount?: BigNumber; totalEthBaseAmount?: BigNumber; ethUsdPrice?: BigNumber; + quoteState: AsyncProcessState; } const mapStateToProps = (state: State, _ownProps: InstantHeadingProps): ConnectedState => ({ selectedAssetAmount: state.selectedAssetAmount, totalEthBaseAmount: oc(state).latestBuyQuote.worstCaseQuoteInfo.totalEthAmount(), ethUsdPrice: state.ethUsdPrice, + quoteState: state.quoteState, }); export const SelectedAssetInstantHeading: React.ComponentClass = connect(mapStateToProps)( diff --git a/packages/instant/src/redux/actions.ts b/packages/instant/src/redux/actions.ts index cf5b39790..e12c728bb 100644 --- a/packages/instant/src/redux/actions.ts +++ b/packages/instant/src/redux/actions.ts @@ -25,6 +25,8 @@ export enum ActionTypes { UPDATE_SELECTED_ASSET_AMOUNT = 'UPDATE_SELECTED_ASSET_AMOUNT', UPDATE_SELECTED_ASSET_BUY_STATE = 'UPDATE_SELECTED_ASSET_BUY_STATE', UPDATE_LATEST_BUY_QUOTE = 'UPDATE_LATEST_BUY_QUOTE', + UPDATE_BUY_QUOTE_STATE_PENDING = 'UPDATE_BUY_QUOTE_STATE_PENDING', + UPDATE_BUY_QUOTE_STATE_FAILURE = 'UPDATE_BUY_QUOTE_STATE_FAILURE', SET_ERROR = 'SET_ERROR', HIDE_ERROR = 'HIDE_ERROR', CLEAR_ERROR = 'CLEAR_ERROR', @@ -36,6 +38,8 @@ export const actions = { updateSelectedAssetBuyState: (buyState: AsyncProcessState) => createAction(ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, buyState), updateLatestBuyQuote: (buyQuote?: BuyQuote) => createAction(ActionTypes.UPDATE_LATEST_BUY_QUOTE, buyQuote), + updateBuyQuoteStatePending: () => createAction(ActionTypes.UPDATE_BUY_QUOTE_STATE_PENDING), + updateBuyQuoteStateFailure: () => createAction(ActionTypes.UPDATE_BUY_QUOTE_STATE_FAILURE), setError: (error?: any) => createAction(ActionTypes.SET_ERROR, error), hideError: () => createAction(ActionTypes.HIDE_ERROR), clearError: () => createAction(ActionTypes.CLEAR_ERROR), diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts index d23064db7..ed8d0f6cf 100644 --- a/packages/instant/src/redux/reducer.ts +++ b/packages/instant/src/redux/reducer.ts @@ -14,9 +14,10 @@ export enum LatestErrorDisplay { export interface State { selectedAssetData?: string; selectedAssetAmount?: BigNumber; - selectedAssetBuyState: AsyncProcessState; + selectedAssetBuyState: AsyncProcessState; // TODO: rename buyOrderState ethUsdPrice?: BigNumber; latestBuyQuote?: BuyQuote; + quoteState: AsyncProcessState; latestError?: any; latestErrorDisplay: LatestErrorDisplay; } @@ -30,6 +31,7 @@ export const INITIAL_STATE: State = { latestBuyQuote: undefined, latestError: undefined, latestErrorDisplay: LatestErrorDisplay.Hidden, + quoteState: AsyncProcessState.NONE, }; export const reducer = (state: State = INITIAL_STATE, action: Action): State => { @@ -48,6 +50,19 @@ export const reducer = (state: State = INITIAL_STATE, action: Action): State => return { ...state, latestBuyQuote: action.data, + quoteState: AsyncProcessState.SUCCESS, + }; + case ActionTypes.UPDATE_BUY_QUOTE_STATE_PENDING: + return { + ...state, + latestBuyQuote: undefined, + quoteState: AsyncProcessState.PENDING, + }; + case ActionTypes.UPDATE_BUY_QUOTE_STATE_FAILURE: + return { + ...state, + latestBuyQuote: undefined, + quoteState: AsyncProcessState.FAILURE, }; case ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE: return { diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index bf3ee392f..f0ffb893b 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -2,10 +2,10 @@ import { AssetProxyId, ObjectMap } from '@0xproject/types'; // Reusable export enum AsyncProcessState { - NONE, - PENDING, - SUCCESS, - FAILURE, + NONE = 'None', + PENDING = 'Pending', + SUCCESS = 'Success', + FAILURE = 'Failure', } export type FunctionType = (...args: any[]) => any; -- cgit v1.2.3