aboutsummaryrefslogtreecommitdiffstats
path: root/packages/instant/src
diff options
context:
space:
mode:
authorfragosti <francesco.agosti93@gmail.com>2018-10-19 06:42:33 +0800
committerfragosti <francesco.agosti93@gmail.com>2018-10-19 06:42:33 +0800
commitc87e68f833a2d8a87846d70a9d4a727b46323eea (patch)
treeeb40dc12694baf607a373f427f32dbd9393ac72f /packages/instant/src
parenta764dfa789ba44e519371b4a1e4569db7f551fb7 (diff)
parent65d85ca5008fe0c307506b388d6ace858122f8ad (diff)
downloaddexon-sol-tools-c87e68f833a2d8a87846d70a9d4a727b46323eea.tar
dexon-sol-tools-c87e68f833a2d8a87846d70a9d4a727b46323eea.tar.gz
dexon-sol-tools-c87e68f833a2d8a87846d70a9d4a727b46323eea.tar.bz2
dexon-sol-tools-c87e68f833a2d8a87846d70a9d4a727b46323eea.tar.lz
dexon-sol-tools-c87e68f833a2d8a87846d70a9d4a727b46323eea.tar.xz
dexon-sol-tools-c87e68f833a2d8a87846d70a9d4a727b46323eea.tar.zst
dexon-sol-tools-c87e68f833a2d8a87846d70a9d4a727b46323eea.zip
Merge branch 'development' of https://github.com/0xProject/0x-monorepo into feature/instant/beta-render-et-al
Diffstat (limited to 'packages/instant/src')
-rw-r--r--packages/instant/src/components/amount_input.tsx2
-rw-r--r--packages/instant/src/components/animations/slide_animations.tsx54
-rw-r--r--packages/instant/src/components/animations/slide_up_and_down_animation.tsx96
-rw-r--r--packages/instant/src/components/asset_amount_input.tsx6
-rw-r--r--packages/instant/src/components/buy_button.tsx2
-rw-r--r--packages/instant/src/components/instant_heading.tsx40
-rw-r--r--packages/instant/src/components/order_details.tsx4
-rw-r--r--packages/instant/src/components/sliding_error.tsx24
-rw-r--r--packages/instant/src/components/zero_ex_instant.tsx4
-rw-r--r--packages/instant/src/components/zero_ex_instant_container.tsx4
-rw-r--r--packages/instant/src/constants.ts2
-rw-r--r--packages/instant/src/containers/latest_buy_quote_order_details.ts4
-rw-r--r--packages/instant/src/containers/latest_error.tsx36
-rw-r--r--packages/instant/src/containers/selected_asset_amount_input.ts51
-rw-r--r--packages/instant/src/containers/selected_asset_buy_button.ts10
-rw-r--r--packages/instant/src/containers/selected_asset_instant_heading.ts5
-rw-r--r--packages/instant/src/data/asset_meta_data_map.ts2
-rw-r--r--packages/instant/src/redux/actions.ts16
-rw-r--r--packages/instant/src/redux/reducer.ts52
-rw-r--r--packages/instant/src/redux/store.ts4
-rw-r--r--packages/instant/src/style/util.ts2
-rw-r--r--packages/instant/src/types.ts13
-rw-r--r--packages/instant/src/util/asset.ts19
-rw-r--r--packages/instant/src/util/coinbase_api.ts2
-rw-r--r--packages/instant/src/util/error.ts64
-rw-r--r--packages/instant/src/util/format.ts24
-rw-r--r--packages/instant/src/util/web3_wrapper.ts2
27 files changed, 362 insertions, 182 deletions
diff --git a/packages/instant/src/components/amount_input.tsx b/packages/instant/src/components/amount_input.tsx
index 7644f5f67..c89fb05ad 100644
--- a/packages/instant/src/components/amount_input.tsx
+++ b/packages/instant/src/components/amount_input.tsx
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import * as React from 'react';
diff --git a/packages/instant/src/components/animations/slide_animations.tsx b/packages/instant/src/components/animations/slide_animations.tsx
new file mode 100644
index 000000000..1f10a2ed6
--- /dev/null
+++ b/packages/instant/src/components/animations/slide_animations.tsx
@@ -0,0 +1,54 @@
+import * as React from 'react';
+
+import { keyframes, styled } from '../../style/theme';
+
+const slideKeyframeGenerator = (fromY: string, toY: string) => keyframes`
+ from {
+ position: relative;
+ top: ${fromY};
+ }
+
+ to {
+ position: relative;
+ top: ${toY};
+ }
+`;
+
+export interface SlideAnimationProps {
+ keyframes: string;
+ animationType: string;
+ animationDirection?: string;
+}
+
+export const SlideAnimation =
+ styled.div <
+ SlideAnimationProps >
+ `
+ animation-name: ${props => props.keyframes};
+ animation-duration: 0.3s;
+ animation-timing-function: ${props => props.animationType};
+ animation-delay: 0s;
+ animation-iteration-count: 1;
+ animation-fill-mode: ${props => props.animationDirection || 'none'};
+ position: relative;
+`;
+
+export interface SlideAnimationComponentProps {
+ downY: string;
+}
+
+export const SlideUpAnimation: React.StatelessComponent<SlideAnimationComponentProps> = props => (
+ <SlideAnimation animationType="ease-in" keyframes={slideKeyframeGenerator(props.downY, '0px')}>
+ {props.children}
+ </SlideAnimation>
+);
+
+export const SlideDownAnimation: React.StatelessComponent<SlideAnimationComponentProps> = props => (
+ <SlideAnimation
+ animationDirection="forwards"
+ animationType="cubic-bezier(0.25, 0.1, 0.25, 1)"
+ keyframes={slideKeyframeGenerator('0px', props.downY)}
+ >
+ {props.children}
+ </SlideAnimation>
+);
diff --git a/packages/instant/src/components/animations/slide_up_and_down_animation.tsx b/packages/instant/src/components/animations/slide_up_and_down_animation.tsx
deleted file mode 100644
index 9c18e0933..000000000
--- a/packages/instant/src/components/animations/slide_up_and_down_animation.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import * as React from 'react';
-
-import { keyframes, styled } from '../../style/theme';
-
-const slideKeyframeGenerator = (fromY: string, toY: string) => keyframes`
- from {
- position: relative;
- top: ${fromY};
- }
-
- to {
- position: relative;
- top: ${toY};
- }
-`;
-
-export interface SlideAnimationProps {
- keyframes: string;
- animationType: string;
- animationDirection?: string;
-}
-
-export const SlideAnimation =
- styled.div <
- SlideAnimationProps >
- `
- animation-name: ${props => props.keyframes};
- animation-duration: 0.3s;
- animation-timing-function: ${props => props.animationType};
- animation-delay: 0s;
- animation-iteration-count: 1;
- animation-fill-mode: ${props => props.animationDirection || 'none'};
- position: relative;
-`;
-
-export interface SlideAnimationComponentProps {
- downY: string;
-}
-
-export const SlideUpAnimationComponent: React.StatelessComponent<SlideAnimationComponentProps> = props => (
- <SlideAnimation animationType="ease-in" keyframes={slideKeyframeGenerator(props.downY, '0px')}>
- {props.children}
- </SlideAnimation>
-);
-
-export const SlideDownAnimationComponent: React.StatelessComponent<SlideAnimationComponentProps> = props => (
- <SlideAnimation
- animationDirection="forwards"
- animationType="cubic-bezier(0.25, 0.1, 0.25, 1)"
- keyframes={slideKeyframeGenerator('0px', props.downY)}
- >
- {props.children}
- </SlideAnimation>
-);
-
-export interface SlideUpAndDownAnimationProps extends SlideAnimationComponentProps {
- delayMs: number;
-}
-
-enum SlideState {
- Up = 'up',
- Down = 'down',
-}
-interface SlideUpAndDownState {
- slideState: SlideState;
-}
-
-export class SlideUpAndDownAnimation extends React.Component<SlideUpAndDownAnimationProps, SlideUpAndDownState> {
- public state = {
- slideState: SlideState.Up,
- };
-
- private _timeoutId?: number;
- public render(): React.ReactNode {
- return this._renderSlide();
- }
- public componentDidMount(): void {
- this._timeoutId = window.setTimeout(() => {
- this.setState({
- slideState: SlideState.Down,
- });
- }, this.props.delayMs);
-
- return;
- }
- public componentWillUnmount(): void {
- if (this._timeoutId) {
- window.clearTimeout(this._timeoutId);
- }
- }
- private _renderSlide(): React.ReactNode {
- const SlideComponent = this.state.slideState === 'up' ? SlideUpAnimationComponent : SlideDownAnimationComponent;
-
- return <SlideComponent downY={this.props.downY}>{this.props.children}</SlideComponent>;
- }
-}
diff --git a/packages/instant/src/components/asset_amount_input.tsx b/packages/instant/src/components/asset_amount_input.tsx
index 9f4b5861a..c03ef1cf3 100644
--- a/packages/instant/src/components/asset_amount_input.tsx
+++ b/packages/instant/src/components/asset_amount_input.tsx
@@ -1,10 +1,10 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import * as React from 'react';
-import { oc } from 'ts-optchain';
import { ColorOption } from '../style/theme';
import { ERC20Asset } from '../types';
+import { assetUtils } from '../util/asset';
import { util } from '../util/util';
import { AmountInput, AmountInputProps } from './amount_input';
@@ -27,7 +27,7 @@ export class AssetAmountInput extends React.Component<AssetAmountInputProps> {
<AmountInput {...rest} onChange={this._handleChange} />
<Container display="inline-block" marginLeft="10px">
<Text fontSize={rest.fontSize} fontColor={ColorOption.white} textTransform="uppercase">
- {oc(asset).metaData.symbol()}
+ {assetUtils.bestNameForAsset(asset)}
</Text>
</Container>
</Container>
diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx
index 191426be1..3ef7c1f5c 100644
--- a/packages/instant/src/components/buy_button.tsx
+++ b/packages/instant/src/components/buy_button.tsx
@@ -1,4 +1,4 @@
-import { AssetBuyer, BuyQuote } from '@0xproject/asset-buyer';
+import { AssetBuyer, BuyQuote } from '@0x/asset-buyer';
import * as _ from 'lodash';
import * as React from 'react';
diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx
index 492c1b2c0..a36d35a93 100644
--- a/packages/instant/src/components/instant_heading.tsx
+++ b/packages/instant/src/components/instant_heading.tsx
@@ -1,9 +1,10 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
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,45 @@ export interface InstantHeadingProps {
selectedAssetAmount?: BigNumber;
totalEthBaseAmount?: BigNumber;
ethUsdPrice?: BigNumber;
+ quoteState: AsyncProcessState;
}
-const displaytotalEthBaseAmount = ({ selectedAssetAmount, totalEthBaseAmount }: InstantHeadingProps): string => {
+const Placeholder = () => (
+ <Text fontWeight="bold" fontColor={ColorOption.white}>
+ &mdash;
+ </Text>
+);
+const displaytotalEthBaseAmount = ({
+ selectedAssetAmount,
+ totalEthBaseAmount,
+}: InstantHeadingProps): React.ReactNode => {
if (_.isUndefined(selectedAssetAmount)) {
return '0 ETH';
}
- return format.ethBaseAmount(totalEthBaseAmount, 4, '...loading');
+ return format.ethBaseAmount(totalEthBaseAmount, 4, <Placeholder />);
};
-const displayUsdAmount = ({ totalEthBaseAmount, selectedAssetAmount, ethUsdPrice }: InstantHeadingProps): string => {
+const displayUsdAmount = ({
+ totalEthBaseAmount,
+ selectedAssetAmount,
+ ethUsdPrice,
+}: InstantHeadingProps): React.ReactNode => {
if (_.isUndefined(selectedAssetAmount)) {
return '$0.00';
}
- return format.ethBaseAmountInUsd(totalEthBaseAmount, ethUsdPrice, 2, '...loading');
+ return format.ethBaseAmountInUsd(totalEthBaseAmount, ethUsdPrice, 2, <Placeholder />);
+};
+
+const loadingOrAmount = (quoteState: AsyncProcessState, amount: React.ReactNode): React.ReactNode => {
+ if (quoteState === AsyncProcessState.PENDING) {
+ return (
+ <Text fontWeight="bold" fontColor={ColorOption.white}>
+ &hellip;loading
+ </Text>
+ );
+ } else {
+ return amount;
+ }
};
export const InstantHeading: React.StatelessComponent<InstantHeadingProps> = props => (
@@ -47,11 +73,11 @@ export const InstantHeading: React.StatelessComponent<InstantHeadingProps> = pro
<Flex direction="column" justify="space-between">
<Container marginBottom="5px">
<Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}>
- {displaytotalEthBaseAmount(props)}
+ {loadingOrAmount(props.quoteState, displaytotalEthBaseAmount(props))}
</Text>
</Container>
<Text fontSize="16px" fontColor={ColorOption.white} opacity={0.7}>
- {displayUsdAmount(props)}
+ {loadingOrAmount(props.quoteState, displayUsdAmount(props))}
</Text>
</Flex>
</Flex>
diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx
index a15ff411b..ad4a87714 100644
--- a/packages/instant/src/components/order_details.tsx
+++ b/packages/instant/src/components/order_details.tsx
@@ -1,5 +1,5 @@
-import { BuyQuoteInfo } from '@0xproject/asset-buyer';
-import { BigNumber } from '@0xproject/utils';
+import { BuyQuoteInfo } from '@0x/asset-buyer';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import * as React from 'react';
import { oc } from 'ts-optchain';
diff --git a/packages/instant/src/components/sliding_error.tsx b/packages/instant/src/components/sliding_error.tsx
index 0237fb7e9..3865a8797 100644
--- a/packages/instant/src/components/sliding_error.tsx
+++ b/packages/instant/src/components/sliding_error.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import { ColorOption } from '../style/theme';
-import { SlideUpAndDownAnimation } from './animations/slide_up_and_down_animation';
+import { SlideDownAnimation, SlideUpAnimation } from './animations/slide_animations';
import { Container, Text } from './ui';
@@ -20,8 +20,8 @@ export const Error: React.StatelessComponent<ErrorProps> = props => (
borderRadius="6px"
marginBottom="10px"
>
- <Container marginRight="5px" display="inline">
- {props.icon}
+ <Container marginRight="5px" display="inline" top="3px" position="relative">
+ <Text fontSize="20px">{props.icon}</Text>
</Container>
<Text fontWeight="500" fontColor={ColorOption.darkOrange}>
{props.message}
@@ -29,8 +29,16 @@ export const Error: React.StatelessComponent<ErrorProps> = props => (
</Container>
);
-export const SlidingError: React.StatelessComponent<ErrorProps> = props => (
- <SlideUpAndDownAnimation downY="120px" delayMs={5000}>
- <Error icon={props.icon} message={props.message} />
- </SlideUpAndDownAnimation>
-);
+export type SlidingDirection = 'up' | 'down';
+export interface SlidingErrorProps extends ErrorProps {
+ direction: SlidingDirection;
+}
+export const SlidingError: React.StatelessComponent<SlidingErrorProps> = props => {
+ const AnimationComponent = props.direction === 'up' ? SlideUpAnimation : SlideDownAnimation;
+
+ return (
+ <AnimationComponent downY="120px">
+ <Error icon={props.icon} message={props.message} />
+ </AnimationComponent>
+ );
+};
diff --git a/packages/instant/src/components/zero_ex_instant.tsx b/packages/instant/src/components/zero_ex_instant.tsx
index cc7b0ecb8..5b75a7556 100644
--- a/packages/instant/src/components/zero_ex_instant.tsx
+++ b/packages/instant/src/components/zero_ex_instant.tsx
@@ -1,5 +1,5 @@
-import { AssetBuyer } from '@0xproject/asset-buyer';
-import { ObjectMap } from '@0xproject/types';
+import { AssetBuyer } from '@0x/asset-buyer';
+import { ObjectMap } from '@0x/types';
import * as React from 'react';
import { Provider } from 'react-redux';
diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx
index 51f9dc63e..cf918d890 100644
--- a/packages/instant/src/components/zero_ex_instant_container.tsx
+++ b/packages/instant/src/components/zero_ex_instant_container.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import { LatestBuyQuoteOrderDetails } from '../containers/latest_buy_quote_order_details';
+import { LatestError } from '../containers/latest_error';
import { SelectedAssetBuyButton } from '../containers/selected_asset_buy_button';
import { SelectedAssetInstantHeading } from '../containers/selected_asset_instant_heading';
@@ -12,6 +13,9 @@ export interface ZeroExInstantContainerProps {}
export const ZeroExInstantContainer: React.StatelessComponent<ZeroExInstantContainerProps> = props => (
<Container width="350px">
+ <Container zIndex={1} position="relative">
+ <LatestError />
+ </Container>
<Container
zIndex={2}
position="relative"
diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts
index b27378d4c..7f4c5a058 100644
--- a/packages/instant/src/constants.ts
+++ b/packages/instant/src/constants.ts
@@ -1,3 +1,3 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
export const BIG_NUMBER_ZERO = new BigNumber(0);
export const ethDecimals = 18;
diff --git a/packages/instant/src/containers/latest_buy_quote_order_details.ts b/packages/instant/src/containers/latest_buy_quote_order_details.ts
index b354c78fa..597bf3088 100644
--- a/packages/instant/src/containers/latest_buy_quote_order_details.ts
+++ b/packages/instant/src/containers/latest_buy_quote_order_details.ts
@@ -1,5 +1,5 @@
-import { BuyQuoteInfo } from '@0xproject/asset-buyer';
-import { BigNumber } from '@0xproject/utils';
+import { BuyQuoteInfo } from '@0x/asset-buyer';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import * as React from 'react';
import { connect } from 'react-redux';
diff --git a/packages/instant/src/containers/latest_error.tsx b/packages/instant/src/containers/latest_error.tsx
new file mode 100644
index 000000000..1d02cab23
--- /dev/null
+++ b/packages/instant/src/containers/latest_error.tsx
@@ -0,0 +1,36 @@
+import * as React from 'react';
+
+import { connect } from 'react-redux';
+
+import { SlidingError } from '../components/sliding_error';
+import { LatestErrorDisplay, State } from '../redux/reducer';
+import { Asset } from '../types';
+import { errorUtil } from '../util/error';
+
+export interface LatestErrorComponentProps {
+ asset?: Asset;
+ latestError?: any;
+ slidingDirection: 'down' | 'up';
+}
+
+export const LatestErrorComponent: React.StatelessComponent<LatestErrorComponentProps> = props => {
+ if (!props.latestError) {
+ return <div />;
+ }
+ const { icon, message } = errorUtil.errorDescription(props.latestError, props.asset);
+ return <SlidingError direction={props.slidingDirection} icon={icon} message={message} />;
+};
+
+interface ConnectedState {
+ asset?: Asset;
+ latestError?: any;
+ slidingDirection: 'down' | 'up';
+}
+export interface LatestErrorProps {}
+const mapStateToProps = (state: State, _ownProps: LatestErrorProps): ConnectedState => ({
+ asset: state.selectedAsset,
+ latestError: state.latestError,
+ slidingDirection: state.latestErrorDisplay === LatestErrorDisplay.Present ? 'up' : 'down',
+});
+
+export const LatestError = connect(mapStateToProps)(LatestErrorComponent);
diff --git a/packages/instant/src/containers/selected_asset_amount_input.ts b/packages/instant/src/containers/selected_asset_amount_input.ts
index b75a22a0e..6cd39b855 100644
--- a/packages/instant/src/containers/selected_asset_amount_input.ts
+++ b/packages/instant/src/containers/selected_asset_amount_input.ts
@@ -1,7 +1,7 @@
-import { AssetBuyer } from '@0xproject/asset-buyer';
-import { AssetProxyId } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { AssetBuyer, 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 * as React from 'react';
import { connect } from 'react-redux';
@@ -11,6 +11,7 @@ import { Action, actions } from '../redux/actions';
import { State } from '../redux/reducer';
import { ColorOption } from '../style/theme';
import { AsyncProcessState, ERC20Asset } from '../types';
+import { errorUtil } from '../util/error';
import { AssetAmountInput } from '../components/asset_amount_input';
@@ -39,7 +40,7 @@ type FinalProps = ConnectedProps & SelectedAssetAmountInputProps;
const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps): ConnectedState => {
const selectedAsset = state.selectedAsset;
- if (_.isUndefined(selectedAsset) || selectedAsset.assetProxyId !== AssetProxyId.ERC20) {
+ if (_.isUndefined(selectedAsset) || selectedAsset.metaData.assetProxyId !== AssetProxyId.ERC20) {
return {
value: state.selectedAssetAmount,
};
@@ -52,22 +53,27 @@ const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps)
};
const updateBuyQuoteAsync = async (
+ assetBuyer: AssetBuyer,
dispatch: Dispatch<Action>,
- assetBuyer?: AssetBuyer,
- asset?: ERC20Asset,
- assetAmount?: BigNumber,
+ asset: ERC20Asset,
+ assetAmount: BigNumber,
): Promise<void> => {
- if (
- _.isUndefined(assetBuyer) ||
- _.isUndefined(assetAmount) ||
- _.isUndefined(asset) ||
- _.isUndefined(asset.metaData)
- ) {
- return;
- }
// get a new buy quote.
const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, asset.metaData.decimals);
- const newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue);
+
+ // mark quote as pending
+ dispatch(actions.updateBuyQuoteStatePending());
+
+ let newBuyQuote: BuyQuote | undefined;
+ try {
+ newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue);
+ } 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));
};
@@ -84,9 +90,14 @@ const mapDispatchToProps = (
// invalidate the last buy quote.
dispatch(actions.updateLatestBuyQuote(undefined));
// reset our buy state
- dispatch(actions.updateSelectedAssetBuyState(AsyncProcessState.NONE));
- // tslint:disable-next-line:no-floating-promises
- debouncedUpdateBuyQuoteAsync(dispatch, assetBuyer, asset, value);
+ dispatch(actions.updatebuyOrderState(AsyncProcessState.NONE));
+
+ if (!_.isUndefined(value) && !_.isUndefined(asset) && !_.isUndefined(assetBuyer)) {
+ // even if it's debounced, give them the illusion it's loading
+ dispatch(actions.updateBuyQuoteStatePending());
+ // tslint:disable-next-line:no-floating-promises
+ debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value);
+ }
},
});
diff --git a/packages/instant/src/containers/selected_asset_buy_button.ts b/packages/instant/src/containers/selected_asset_buy_button.ts
index 4118932b2..99f971321 100644
--- a/packages/instant/src/containers/selected_asset_buy_button.ts
+++ b/packages/instant/src/containers/selected_asset_buy_button.ts
@@ -1,4 +1,4 @@
-import { AssetBuyer, BuyQuote } from '@0xproject/asset-buyer';
+import { AssetBuyer, BuyQuote } from '@0x/asset-buyer';
import * as _ from 'lodash';
import * as React from 'react';
import { connect } from 'react-redux';
@@ -41,14 +41,14 @@ const textForState = (state: AsyncProcessState): string => {
const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyButtonProps): ConnectedState => ({
assetBuyer: state.assetBuyer,
- text: textForState(state.selectedAssetBuyState),
+ text: textForState(state.buyOrderState),
buyQuote: state.latestBuyQuote,
});
const mapDispatchToProps = (dispatch: Dispatch<Action>, ownProps: SelectedAssetBuyButtonProps): ConnectedDispatch => ({
- onClick: buyQuote => dispatch(actions.updateSelectedAssetBuyState(AsyncProcessState.PENDING)),
- onBuySuccess: buyQuote => dispatch(actions.updateSelectedAssetBuyState(AsyncProcessState.SUCCESS)),
- onBuyFailure: buyQuote => dispatch(actions.updateSelectedAssetBuyState(AsyncProcessState.FAILURE)),
+ onClick: buyQuote => dispatch(actions.updatebuyOrderState(AsyncProcessState.PENDING)),
+ onBuySuccess: buyQuote => dispatch(actions.updatebuyOrderState(AsyncProcessState.SUCCESS)),
+ onBuyFailure: buyQuote => dispatch(actions.updatebuyOrderState(AsyncProcessState.FAILURE)),
});
export const SelectedAssetBuyButton: React.ComponentClass<SelectedAssetBuyButtonProps> = connect(
diff --git a/packages/instant/src/containers/selected_asset_instant_heading.ts b/packages/instant/src/containers/selected_asset_instant_heading.ts
index c97cfe11a..0509db5da 100644
--- a/packages/instant/src/containers/selected_asset_instant_heading.ts
+++ b/packages/instant/src/containers/selected_asset_instant_heading.ts
@@ -1,10 +1,11 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import * as React from 'react';
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<InstantHeadingProps> = connect(mapStateToProps)(
diff --git a/packages/instant/src/data/asset_meta_data_map.ts b/packages/instant/src/data/asset_meta_data_map.ts
index 7d83865f1..3a820a0c4 100644
--- a/packages/instant/src/data/asset_meta_data_map.ts
+++ b/packages/instant/src/data/asset_meta_data_map.ts
@@ -1,4 +1,4 @@
-import { AssetProxyId, ObjectMap } from '@0xproject/types';
+import { AssetProxyId, ObjectMap } from '@0x/types';
import { AssetMetaData } from '../types';
diff --git a/packages/instant/src/redux/actions.ts b/packages/instant/src/redux/actions.ts
index fe055b75f..bc75ce66c 100644
--- a/packages/instant/src/redux/actions.ts
+++ b/packages/instant/src/redux/actions.ts
@@ -1,5 +1,5 @@
-import { BuyQuote } from '@0xproject/asset-buyer';
-import { BigNumber } from '@0xproject/utils';
+import { BuyQuote } from '@0x/asset-buyer';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { ActionsUnion, AsyncProcessState } from '../types';
@@ -26,13 +26,23 @@ export enum ActionTypes {
UPDATE_SELECTED_ASSET_BUY_STATE = 'UPDATE_SELECTED_ASSET_BUY_STATE',
UPDATE_LATEST_BUY_QUOTE = 'UPDATE_LATEST_BUY_QUOTE',
UPDATE_SELECTED_ASSET = 'UPDATE_SELECTED_ASSET',
+ 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',
}
export const actions = {
updateEthUsdPrice: (price?: BigNumber) => createAction(ActionTypes.UPDATE_ETH_USD_PRICE, price),
updateSelectedAssetAmount: (amount?: BigNumber) => createAction(ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, amount),
- updateSelectedAssetBuyState: (buyState: AsyncProcessState) =>
+ updatebuyOrderState: (buyState: AsyncProcessState) =>
createAction(ActionTypes.UPDATE_SELECTED_ASSET_BUY_STATE, buyState),
updateLatestBuyQuote: (buyQuote?: BuyQuote) => createAction(ActionTypes.UPDATE_LATEST_BUY_QUOTE, buyQuote),
updateSelectedAsset: (assetData?: string) => createAction(ActionTypes.UPDATE_SELECTED_ASSET, assetData),
+ 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 9922131b4..657bd0e40 100644
--- a/packages/instant/src/redux/reducer.ts
+++ b/packages/instant/src/redux/reducer.ts
@@ -1,6 +1,6 @@
-import { AssetBuyer, BuyQuote } from '@0xproject/asset-buyer';
-import { ObjectMap } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { AssetBuyer, BuyQuote } from '@0x/asset-buyer';
+import { ObjectMap } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { Asset, AssetMetaData, AsyncProcessState } from '../types';
@@ -8,22 +8,32 @@ import { assetUtils } from '../util/asset';
import { Action, ActionTypes } from './actions';
+export enum LatestErrorDisplay {
+ Present,
+ Hidden,
+}
export interface State {
assetBuyer?: AssetBuyer;
assetMetaDataMap: ObjectMap<AssetMetaData>;
selectedAsset?: Asset;
selectedAssetAmount?: BigNumber;
- selectedAssetBuyState: AsyncProcessState;
+ buyOrderState: AsyncProcessState;
ethUsdPrice?: BigNumber;
latestBuyQuote?: BuyQuote;
+ quoteState: AsyncProcessState;
+ latestError?: any;
+ latestErrorDisplay: LatestErrorDisplay;
}
export const INITIAL_STATE: State = {
selectedAssetAmount: undefined,
assetMetaDataMap: {},
- selectedAssetBuyState: AsyncProcessState.NONE,
+ buyOrderState: AsyncProcessState.NONE,
ethUsdPrice: undefined,
latestBuyQuote: undefined,
+ latestError: undefined,
+ latestErrorDisplay: LatestErrorDisplay.Hidden,
+ quoteState: AsyncProcessState.NONE,
};
// TODO: Figure out why there is an INITIAL_STATE key in the store...
@@ -43,11 +53,41 @@ 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 {
...state,
- selectedAssetBuyState: action.data,
+ buyOrderState: action.data,
+ };
+ case ActionTypes.SET_ERROR:
+ return {
+ ...state,
+ latestError: action.data,
+ latestErrorDisplay: LatestErrorDisplay.Present,
+ };
+ case ActionTypes.HIDE_ERROR:
+ return {
+ ...state,
+ latestErrorDisplay: LatestErrorDisplay.Hidden,
+ };
+ case ActionTypes.CLEAR_ERROR:
+ return {
+ ...state,
+ latestError: undefined,
+ latestErrorDisplay: LatestErrorDisplay.Hidden,
};
case ActionTypes.UPDATE_SELECTED_ASSET:
const newSelectedAssetData = action.data;
diff --git a/packages/instant/src/redux/store.ts b/packages/instant/src/redux/store.ts
index fc943f1be..505234299 100644
--- a/packages/instant/src/redux/store.ts
+++ b/packages/instant/src/redux/store.ts
@@ -1,5 +1,6 @@
import * as _ from 'lodash';
import { createStore, Store as ReduxStore } from 'redux';
+import { devToolsEnhancer } from 'redux-devtools-extension/developmentOnly';
import { INITIAL_STATE, reducer, State } from './reducer';
@@ -11,6 +12,7 @@ export const store = {
INITIAL_STATE,
...withState,
};
- return createStore(reducer, allInitialState);
+ return createStore(reducer, allInitialState, devToolsEnhancer({}));
},
};
+
diff --git a/packages/instant/src/style/util.ts b/packages/instant/src/style/util.ts
index c9df0f834..3e38c4a7d 100644
--- a/packages/instant/src/style/util.ts
+++ b/packages/instant/src/style/util.ts
@@ -1,4 +1,4 @@
-import { ObjectMap } from '@0xproject/types';
+import { ObjectMap } from '@0x/types';
import * as _ from 'lodash';
export const cssRuleIfExists = (props: ObjectMap<any>, rule: string): string => {
diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts
index 867605573..0454bceea 100644
--- a/packages/instant/src/types.ts
+++ b/packages/instant/src/types.ts
@@ -1,11 +1,11 @@
-import { AssetProxyId, ObjectMap } from '@0xproject/types';
+import { AssetProxyId, ObjectMap } from '@0x/types';
// Reusable
export enum AsyncProcessState {
- NONE,
- PENDING,
- SUCCESS,
- FAILURE,
+ NONE = 'None',
+ PENDING = 'Pending',
+ SUCCESS = 'Success',
+ FAILURE = 'Failure',
}
export type FunctionType = (...args: any[]) => any;
@@ -29,18 +29,15 @@ export interface ERC721AssetMetaData {
export type AssetMetaData = ERC20AssetMetaData | ERC721AssetMetaData;
export interface ERC20Asset {
- assetProxyId: AssetProxyId.ERC20;
assetData: string;
metaData: ERC20AssetMetaData;
}
export interface ERC721Asset {
- assetProxyId: AssetProxyId.ERC721;
assetData: string;
metaData: ERC721AssetMetaData;
}
export interface Asset {
- assetProxyId: AssetProxyId;
assetData: string;
metaData: AssetMetaData;
}
diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts
index ec22276ae..edeac0da3 100644
--- a/packages/instant/src/util/asset.ts
+++ b/packages/instant/src/util/asset.ts
@@ -1,5 +1,5 @@
-import { assetDataUtils } from '@0xproject/order-utils';
-import { AssetProxyId, ObjectMap } from '@0xproject/types';
+import { assetDataUtils } from '@0x/order-utils';
+import { AssetProxyId, ObjectMap } from '@0x/types';
import * as _ from 'lodash';
import { assetDataNetworkMapping } from '../data/asset_data_network_mapping';
@@ -8,7 +8,6 @@ import { Asset, AssetMetaData, Network, ZeroExInstantError } from '../types';
export const assetUtils = {
createAssetFromAssetData: (assetData: string, assetMetaDataMap: ObjectMap<AssetMetaData>): Asset => {
return {
- assetProxyId: assetDataUtils.decodeAssetProxyId(assetData),
assetData,
metaData: assetUtils.getMetaDataOrThrow(assetData, assetMetaDataMap),
};
@@ -31,4 +30,18 @@ export const assetUtils = {
}
return metaData;
},
+ bestNameForAsset: (asset?: Asset, defaultName: string = '???'): string => {
+ if (_.isUndefined(asset)) {
+ return defaultName;
+ }
+ const metaData = asset.metaData;
+ switch (metaData.assetProxyId) {
+ case AssetProxyId.ERC20:
+ return metaData.symbol;
+ case AssetProxyId.ERC721:
+ return metaData.name;
+ default:
+ return defaultName;
+ }
+ },
};
diff --git a/packages/instant/src/util/coinbase_api.ts b/packages/instant/src/util/coinbase_api.ts
index 94a5d3c80..080421f98 100644
--- a/packages/instant/src/util/coinbase_api.ts
+++ b/packages/instant/src/util/coinbase_api.ts
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
const baseEndpoint = 'https://api.coinbase.com/v2';
export const coinbaseApi = {
diff --git a/packages/instant/src/util/error.ts b/packages/instant/src/util/error.ts
new file mode 100644
index 000000000..40fd24c7e
--- /dev/null
+++ b/packages/instant/src/util/error.ts
@@ -0,0 +1,64 @@
+import { AssetBuyerError } from '@0x/asset-buyer';
+import { Dispatch } from 'redux';
+
+import { Action, actions } from '../redux/actions';
+import { Asset } from '../types';
+
+import { assetUtils } from './asset';
+
+class ErrorFlasher {
+ private _timeoutId?: number;
+ public flashNewError(dispatch: Dispatch<Action>, error: any, delayMs: number = 7000): void {
+ this._clearTimeout();
+
+ // dispatch new message
+ dispatch(actions.setError(error));
+
+ this._timeoutId = window.setTimeout(() => {
+ dispatch(actions.hideError());
+ }, delayMs);
+ }
+ public clearError(dispatch: Dispatch<Action>): void {
+ this._clearTimeout();
+ dispatch(actions.hideError());
+ }
+ private _clearTimeout(): void {
+ if (this._timeoutId) {
+ window.clearTimeout(this._timeoutId);
+ }
+ }
+}
+
+const humanReadableMessageForError = (error: Error, asset?: Asset): string | undefined => {
+ const hasInsufficientLiquidity =
+ error.message === AssetBuyerError.InsufficientAssetLiquidity ||
+ error.message === AssetBuyerError.InsufficientZrxLiquidity;
+ if (hasInsufficientLiquidity) {
+ const assetName = assetUtils.bestNameForAsset(asset, 'of this asset');
+ return `Not enough ${assetName} available`;
+ }
+
+ if (
+ error.message === AssetBuyerError.StandardRelayerApiError ||
+ error.message.startsWith(AssetBuyerError.AssetUnavailable)
+ ) {
+ const assetName = assetUtils.bestNameForAsset(asset, 'This asset');
+ return `${assetName} is currently unavailable`;
+ }
+
+ return undefined;
+};
+
+export const errorUtil = {
+ errorFlasher: new ErrorFlasher(),
+ errorDescription: (error?: any, asset?: Asset): { icon: string; message: string } => {
+ let bestMessage: string | undefined;
+ if (error instanceof Error) {
+ bestMessage = humanReadableMessageForError(error, asset);
+ }
+ return {
+ icon: '😢',
+ message: bestMessage || 'Something went wrong...',
+ };
+ },
+};
diff --git a/packages/instant/src/util/format.ts b/packages/instant/src/util/format.ts
index b62c968fb..8482b1526 100644
--- a/packages/instant/src/util/format.ts
+++ b/packages/instant/src/util/format.ts
@@ -1,18 +1,26 @@
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import { ethDecimals } from '../constants';
export const format = {
- ethBaseAmount: (ethBaseAmount?: BigNumber, decimalPlaces: number = 4, defaultText: string = '0 ETH'): string => {
+ ethBaseAmount: (
+ ethBaseAmount?: BigNumber,
+ decimalPlaces: number = 4,
+ defaultText: React.ReactNode = '0 ETH',
+ ): React.ReactNode => {
if (_.isUndefined(ethBaseAmount)) {
return defaultText;
}
const ethUnitAmount = Web3Wrapper.toUnitAmount(ethBaseAmount, ethDecimals);
return format.ethUnitAmount(ethUnitAmount, decimalPlaces);
},
- ethUnitAmount: (ethUnitAmount?: BigNumber, decimalPlaces: number = 4, defaultText: string = '0 ETH'): string => {
+ ethUnitAmount: (
+ ethUnitAmount?: BigNumber,
+ decimalPlaces: number = 4,
+ defaultText: React.ReactNode = '0 ETH',
+ ): React.ReactNode => {
if (_.isUndefined(ethUnitAmount)) {
return defaultText;
}
@@ -23,8 +31,8 @@ export const format = {
ethBaseAmount?: BigNumber,
ethUsdPrice?: BigNumber,
decimalPlaces: number = 2,
- defaultText: string = '$0.00',
- ): string => {
+ defaultText: React.ReactNode = '$0.00',
+ ): React.ReactNode => {
if (_.isUndefined(ethBaseAmount) || _.isUndefined(ethUsdPrice)) {
return defaultText;
}
@@ -35,8 +43,8 @@ export const format = {
ethUnitAmount?: BigNumber,
ethUsdPrice?: BigNumber,
decimalPlaces: number = 2,
- defaultText: string = '$0.00',
- ): string => {
+ defaultText: React.ReactNode = '$0.00',
+ ): React.ReactNode => {
if (_.isUndefined(ethUnitAmount) || _.isUndefined(ethUsdPrice)) {
return defaultText;
}
diff --git a/packages/instant/src/util/web3_wrapper.ts b/packages/instant/src/util/web3_wrapper.ts
index d7e43521f..24dcd9076 100644
--- a/packages/instant/src/util/web3_wrapper.ts
+++ b/packages/instant/src/util/web3_wrapper.ts
@@ -1,4 +1,4 @@
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import { getProvider } from './provider';