aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrandon Millman <brandon.millman@gmail.com>2018-11-09 08:09:38 +0800
committerBrandon Millman <brandon.millman@gmail.com>2018-11-09 08:09:38 +0800
commit211163b3724090347dba736cbcdcb2b1ec252bd0 (patch)
tree80f3b8e23daba4a12e4afc55d7a345678ca04795
parentc84e163edb329b478749cad09f222cc12e3698fe (diff)
parent117e2f583ff44bdb63340a2134edea0f3ecb77b3 (diff)
downloaddexon-sol-tools-211163b3724090347dba736cbcdcb2b1ec252bd0.tar.gz
dexon-sol-tools-211163b3724090347dba736cbcdcb2b1ec252bd0.tar.zst
dexon-sol-tools-211163b3724090347dba736cbcdcb2b1ec252bd0.zip
Merge branch 'development' into feature/instant/account-state-change
* development: [instant] Viewport specific errors (#1228) Added more comments Include wholeNumberSchema in sra-spec schemas chore(instant): fix linter Fix isNode fix(instant): update buy quote at start up in the case of default amount fix: apply css reset to overlay as well fix(website): turn off production flag when building locally chore: linter fix: progress bar fix: use fontSize prop in button feat: make instant resistant to external styles feat: add faux externall css file Add upstream issue Use const require instead of import Fix tslint issues Use detect-node Set curstom inspect printer in BigNumber
-rw-r--r--.circleci/config.yml2
-rw-r--r--packages/instant/public/external.css25
-rw-r--r--packages/instant/public/index.html1
-rw-r--r--packages/instant/src/components/animations/position_animation.tsx66
-rw-r--r--packages/instant/src/components/animations/slide_animation.tsx12
-rw-r--r--packages/instant/src/components/buy_button.tsx13
-rw-r--r--packages/instant/src/components/buy_order_state_buttons.tsx7
-rw-r--r--packages/instant/src/components/css_reset.tsx33
-rw-r--r--packages/instant/src/components/placing_order_button.tsx7
-rw-r--r--packages/instant/src/components/secondary_button.tsx7
-rw-r--r--packages/instant/src/components/sliding_error.tsx37
-rw-r--r--packages/instant/src/components/sliding_panel.tsx3
-rw-r--r--packages/instant/src/components/timed_progress_bar.tsx14
-rw-r--r--packages/instant/src/components/ui/button.tsx61
-rw-r--r--packages/instant/src/components/ui/circle.tsx10
-rw-r--r--packages/instant/src/components/ui/container.tsx75
-rw-r--r--packages/instant/src/components/ui/flex.tsx21
-rw-r--r--packages/instant/src/components/ui/icon.tsx20
-rw-r--r--packages/instant/src/components/ui/input.tsx23
-rw-r--r--packages/instant/src/components/ui/overlay.tsx16
-rw-r--r--packages/instant/src/components/ui/text.tsx52
-rw-r--r--packages/instant/src/components/zero_ex_instant.tsx10
-rw-r--r--packages/instant/src/components/zero_ex_instant_container.tsx62
-rw-r--r--packages/instant/src/components/zero_ex_instant_provider.tsx2
-rw-r--r--packages/instant/src/constants.ts1
-rw-r--r--packages/instant/src/containers/selected_erc20_asset_amount_input.ts56
-rw-r--r--packages/instant/src/index.umd.ts3
-rw-r--r--packages/instant/src/redux/async_data.ts21
-rw-r--r--packages/instant/src/style/media.ts38
-rw-r--r--packages/instant/src/style/theme.ts4
-rw-r--r--packages/instant/src/style/z_index.ts3
-rw-r--r--packages/instant/src/util/buy_quote_updater.ts56
-rw-r--r--packages/sra-spec/src/json-schemas.ts2
-rw-r--r--packages/utils/src/configured_bignumber.ts23
-rw-r--r--packages/website/package.json9
35 files changed, 504 insertions, 291 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 7811b9b34..0ab512f58 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -31,7 +31,7 @@ jobs:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- - run: cd packages/website && yarn build
+ - run: cd packages/website && yarn build:prod
test-contracts-ganache:
docker:
- image: circleci/node:9
diff --git a/packages/instant/public/external.css b/packages/instant/public/external.css
new file mode 100644
index 000000000..cab11112a
--- /dev/null
+++ b/packages/instant/public/external.css
@@ -0,0 +1,25 @@
+/*
+ CSS file meant to represent an external (integrators) stylesheet and
+ help ensure that instant looks consistent across environments.
+*/
+
+button {
+ font-size: 50px;
+ height: 200px;
+ background-color: red;
+}
+
+input {
+ padding: 100px;
+ font-size: 50px;
+ height: 100px;
+}
+
+div {
+ padding: 3px;
+}
+
+p {
+ background-color: green;
+ margin: 10px;
+}
diff --git a/packages/instant/public/index.html b/packages/instant/public/index.html
index 033a125a4..f6c809e33 100644
--- a/packages/instant/public/index.html
+++ b/packages/instant/public/index.html
@@ -5,6 +5,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>0x Instant Dev Environment</title>
+ <link rel="stylesheet" href="/external.css">
<script type="text/javascript" src="/main.bundle.js" charset="utf-8"></script>
<script type="text/javascript" src="https://unpkg.com/jsuri@1.3.1/Uri.js" charset="utf-8"></script>
<script type="text/javascript" src="https://unpkg.com/bignumber.js@4.1.0/bignumber.js" charset="utf-8"></script>
diff --git a/packages/instant/src/components/animations/position_animation.tsx b/packages/instant/src/components/animations/position_animation.tsx
index 4bb21befb..576d29c07 100644
--- a/packages/instant/src/components/animations/position_animation.tsx
+++ b/packages/instant/src/components/animations/position_animation.tsx
@@ -1,5 +1,6 @@
-import { Keyframes } from 'styled-components';
+import { InterpolationValue } from 'styled-components';
+import { media, OptionallyScreenSpecific, stylesForMedia } from '../../style/media';
import { css, keyframes, styled } from '../../style/theme';
export interface TransitionInfo {
@@ -51,30 +52,57 @@ export interface PositionAnimationSettings {
right?: TransitionInfo;
timingFunction: string;
duration?: string;
+ position?: string;
}
-export interface PositionAnimationProps extends PositionAnimationSettings {
- position: string;
+const generatePositionAnimationCss = (positionSettings: PositionAnimationSettings) => {
+ return css`
+ animation-name: ${slideKeyframeGenerator(
+ positionSettings.position || 'relative',
+ positionSettings.top,
+ positionSettings.bottom,
+ positionSettings.left,
+ positionSettings.right,
+ )};
+ animation-duration: ${positionSettings.duration || '0.3s'};
+ animation-timing-function: ${positionSettings.timingFunction};
+ animation-delay: 0s;
+ animation-iteration-count: 1;
+ animation-fill-mode: forwards;
+ position: ${positionSettings.position || 'relative'};
+ width: 100%;
+ `;
+};
+
+export interface PositionAnimationProps {
+ positionSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
+ zIndex?: OptionallyScreenSpecific<number>;
}
+const defaultAnimation = (positionSettings: OptionallyScreenSpecific<PositionAnimationSettings>) => {
+ const bestDefault = 'default' in positionSettings ? positionSettings.default : positionSettings;
+ return generatePositionAnimationCss(bestDefault);
+};
+const animationForSize = (
+ positionSettings: OptionallyScreenSpecific<PositionAnimationSettings>,
+ sizeKey: 'sm' | 'md' | 'lg',
+ mediaFn: (...args: any[]) => InterpolationValue[],
+) => {
+ // checking default makes sure we have a PositionAnimationSettings object
+ // and then we check to see if we have a setting for the specific `sizeKey`
+ const animationSettingsForSize = 'default' in positionSettings && positionSettings[sizeKey];
+ return animationSettingsForSize && mediaFn`${generatePositionAnimationCss(animationSettingsForSize)}`;
+};
+
export const PositionAnimation =
styled.div <
PositionAnimationProps >
`
- animation-name: ${props =>
- css`
- ${slideKeyframeGenerator(props.position, props.top, props.bottom, props.left, props.right)};
- `};
- animation-duration: ${props => props.duration || '0.3s'};
- animation-timing-function: ${props => props.timingFunction};
- animation-delay: 0s;
- animation-iteration-count: 1;
- animation-fill-mode: forwards;
- position: ${props => props.position};
- height: 100%;
- width: 100%;
+ && {
+ ${props => props.zIndex && stylesForMedia<number>('z-index', props.zIndex)}
+ ${props => defaultAnimation(props.positionSettings)}
+ ${props => animationForSize(props.positionSettings, 'sm', media.small)}
+ ${props => animationForSize(props.positionSettings, 'md', media.medium)}
+ ${props => animationForSize(props.positionSettings, 'lg', media.large)}
+ }
`;
-
-PositionAnimation.defaultProps = {
- position: 'relative',
-};
diff --git a/packages/instant/src/components/animations/slide_animation.tsx b/packages/instant/src/components/animations/slide_animation.tsx
index 66a314c7f..122229dee 100644
--- a/packages/instant/src/components/animations/slide_animation.tsx
+++ b/packages/instant/src/components/animations/slide_animation.tsx
@@ -1,22 +1,24 @@
import * as React from 'react';
+import { OptionallyScreenSpecific } from '../../style/media';
+
import { PositionAnimation, PositionAnimationSettings } from './position_animation';
export type SlideAnimationState = 'slidIn' | 'slidOut' | 'none';
export interface SlideAnimationProps {
- position: string;
animationState: SlideAnimationState;
- slideInSettings: PositionAnimationSettings;
- slideOutSettings: PositionAnimationSettings;
+ slideInSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
+ slideOutSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
+ zIndex?: OptionallyScreenSpecific<number>;
}
export const SlideAnimation: React.StatelessComponent<SlideAnimationProps> = props => {
if (props.animationState === 'none') {
return <React.Fragment>{props.children}</React.Fragment>;
}
- const propsToUse = props.animationState === 'slidIn' ? props.slideInSettings : props.slideOutSettings;
+ const positionSettings = props.animationState === 'slidIn' ? props.slideInSettings : props.slideOutSettings;
return (
- <PositionAnimation position={props.position} {...propsToUse}>
+ <PositionAnimation positionSettings={positionSettings} zIndex={props.zIndex}>
{props.children}
</PositionAnimation>
);
diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx
index 9a6e22ea9..877ab275c 100644
--- a/packages/instant/src/components/buy_button.tsx
+++ b/packages/instant/src/components/buy_button.tsx
@@ -12,7 +12,6 @@ import { gasPriceEstimator } from '../util/gas_price_estimator';
import { util } from '../util/util';
import { Button } from './ui/button';
-import { Text } from './ui/text';
export interface BuyButtonProps {
accountAddress?: string;
@@ -39,10 +38,14 @@ export class BuyButton extends React.Component<BuyButtonProps> {
const { buyQuote, accountAddress } = this.props;
const shouldDisableButton = _.isUndefined(buyQuote) || _.isUndefined(accountAddress);
return (
- <Button width="100%" onClick={this._handleClick} isDisabled={shouldDisableButton}>
- <Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
- Buy
- </Text>
+ <Button
+ width="100%"
+ onClick={this._handleClick}
+ isDisabled={shouldDisableButton}
+ fontColor={ColorOption.white}
+ fontSize="20px"
+ >
+ Buy
</Button>
);
}
diff --git a/packages/instant/src/components/buy_order_state_buttons.tsx b/packages/instant/src/components/buy_order_state_buttons.tsx
index b46ff7714..6041bf4f5 100644
--- a/packages/instant/src/components/buy_order_state_buttons.tsx
+++ b/packages/instant/src/components/buy_order_state_buttons.tsx
@@ -12,7 +12,6 @@ import { SecondaryButton } from './secondary_button';
import { Button } from './ui/button';
import { Flex } from './ui/flex';
-import { Text } from './ui/text';
export interface BuyOrderStateButtonProps {
accountAddress?: string;
@@ -36,10 +35,8 @@ export const BuyOrderStateButtons: React.StatelessComponent<BuyOrderStateButtonP
if (props.buyOrderProcessingState === OrderProcessState.Failure) {
return (
<Flex justify="space-between">
- <Button width="48%" onClick={props.onRetry}>
- <Text fontColor={ColorOption.white} fontWeight={600} fontSize="16px">
- Back
- </Text>
+ <Button width="48%" onClick={props.onRetry} fontColor={ColorOption.white} fontSize="16px">
+ Back
</Button>
<SecondaryButton width="48%" onClick={props.onViewTransaction}>
Details
diff --git a/packages/instant/src/components/css_reset.tsx b/packages/instant/src/components/css_reset.tsx
new file mode 100644
index 000000000..a1dd2e05c
--- /dev/null
+++ b/packages/instant/src/components/css_reset.tsx
@@ -0,0 +1,33 @@
+import { INJECTED_DIV_CLASS } from '../constants';
+import { createGlobalStyle } from '../style/theme';
+
+export interface CSSResetProps {}
+
+/*
+* Derived from
+* https://github.com/jtrost/Complete-CSS-Reset
+*/
+export const CSSReset = createGlobalStyle`
+ .${INJECTED_DIV_CLASS} {
+ a, abbr, area, article, aside, audio, b, bdo, blockquote, body, button,
+ canvas, caption, cite, code, col, colgroup, command, datalist, dd, del,
+ details, dialog, dfn, div, dl, dt, em, embed, fieldset, figure, form,
+ h1, h2, h3, h4, h5, h6, head, header, hgroup, hr, html, i, iframe, img,
+ input, ins, keygen, kbd, label, legend, li, map, mark, menu, meter, nav,
+ noscript, object, ol, optgroup, option, output, p, param, pre, progress,
+ q, rp, rt, ruby, samp, section, select, small, span, strong, sub, sup,
+ table, tbody, td, textarea, tfoot, th, thead, time, tr, ul, var, video {
+ background: transparent;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ margin: 0;
+ outline: none;
+ padding: 0;
+ text-align: left;
+ text-decoration: none;
+ vertical-align: baseline;
+ z-index: 1;
+ }
+ }
+`;
diff --git a/packages/instant/src/components/placing_order_button.tsx b/packages/instant/src/components/placing_order_button.tsx
index 4232e6c22..d774d7d27 100644
--- a/packages/instant/src/components/placing_order_button.tsx
+++ b/packages/instant/src/components/placing_order_button.tsx
@@ -5,15 +5,12 @@ import { ColorOption } from '../style/theme';
import { Button } from './ui/button';
import { Container } from './ui/container';
import { Spinner } from './ui/spinner';
-import { Text } from './ui/text';
export const PlacingOrderButton: React.StatelessComponent<{}> = props => (
- <Button isDisabled={true} width="100%">
+ <Button isDisabled={true} width="100%" fontColor={ColorOption.white} fontSize="20px">
<Container display="inline-block" position="relative" top="3px" marginRight="8px">
<Spinner widthPx={20} heightPx={20} />
</Container>
- <Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px">
- Placing Order&hellip;
- </Text>
+ Placing Order&hellip;
</Button>
);
diff --git a/packages/instant/src/components/secondary_button.tsx b/packages/instant/src/components/secondary_button.tsx
index 583058b5b..df0539606 100644
--- a/packages/instant/src/components/secondary_button.tsx
+++ b/packages/instant/src/components/secondary_button.tsx
@@ -4,7 +4,6 @@ import * as React from 'react';
import { ColorOption } from '../style/theme';
import { Button, ButtonProps } from './ui/button';
-import { Text } from './ui/text';
export interface SecondaryButtonProps extends ButtonProps {}
@@ -16,11 +15,11 @@ export const SecondaryButton: React.StatelessComponent<SecondaryButtonProps> = p
borderColor={ColorOption.lightGrey}
width={props.width}
onClick={props.onClick}
+ fontColor={ColorOption.primaryColor}
+ fontSize="16px"
{...buttonProps}
>
- <Text fontColor={ColorOption.primaryColor} fontWeight={600} fontSize="16px">
- {props.children}
- </Text>
+ {props.children}
</Button>
);
};
diff --git a/packages/instant/src/components/sliding_error.tsx b/packages/instant/src/components/sliding_error.tsx
index a923b9932..462199d78 100644
--- a/packages/instant/src/components/sliding_error.tsx
+++ b/packages/instant/src/components/sliding_error.tsx
@@ -1,6 +1,8 @@
import * as React from 'react';
+import { ScreenSpecification } from '../style/media';
import { ColorOption } from '../style/theme';
+import { zIndex } from '../style/z_index';
import { PositionAnimationSettings } from './animations/position_animation';
import { SlideAnimation, SlideAnimationState } from './animations/slide_animation';
@@ -21,6 +23,7 @@ export const Error: React.StatelessComponent<ErrorProps> = props => (
backgroundColor={ColorOption.lightOrange}
width="100%"
borderRadius="6px"
+ marginTop="10px"
marginBottom="10px"
>
<Flex justify="flex-start">
@@ -39,25 +42,51 @@ export interface SlidingErrorProps extends ErrorProps {
}
export const SlidingError: React.StatelessComponent<SlidingErrorProps> = props => {
const slideAmount = '120px';
- const slideUpSettings: PositionAnimationSettings = {
+
+ const desktopSlideIn: PositionAnimationSettings = {
timingFunction: 'ease-in',
top: {
from: slideAmount,
to: '0px',
},
+ position: 'relative',
};
- const slideDownSettings: PositionAnimationSettings = {
+ const desktopSlideOut: PositionAnimationSettings = {
timingFunction: 'cubic-bezier(0.25, 0.1, 0.25, 1)',
top: {
from: '0px',
to: slideAmount,
},
+ position: 'relative',
+ };
+
+ const mobileSlideIn: PositionAnimationSettings = {
+ duration: '0.5s',
+ timingFunction: 'ease-in',
+ top: { from: '-120px', to: '0px' },
+ position: 'fixed',
+ };
+ const moblieSlideOut: PositionAnimationSettings = {
+ duration: '0.5s',
+ timingFunction: 'ease-in',
+ top: { from: '0px', to: '-120px' },
+ position: 'fixed',
+ };
+
+ const slideUpSettings: ScreenSpecification<PositionAnimationSettings> = {
+ default: desktopSlideIn,
+ sm: mobileSlideIn,
};
+ const slideOutSettings: ScreenSpecification<PositionAnimationSettings> = {
+ default: desktopSlideOut,
+ sm: moblieSlideOut,
+ };
+
return (
<SlideAnimation
- position="relative"
slideInSettings={slideUpSettings}
- slideOutSettings={slideDownSettings}
+ slideOutSettings={slideOutSettings}
+ zIndex={{ sm: zIndex.errorPopUp, default: zIndex.errorPopBehind }}
animationState={props.animationState}
>
<Error icon={props.icon} message={props.message} />
diff --git a/packages/instant/src/components/sliding_panel.tsx b/packages/instant/src/components/sliding_panel.tsx
index 7ef28e09c..a5d15c401 100644
--- a/packages/instant/src/components/sliding_panel.tsx
+++ b/packages/instant/src/components/sliding_panel.tsx
@@ -51,6 +51,7 @@ export const SlidingPanel: React.StatelessComponent<SlidingPanelProps> = props =
from: slideAmount,
to: '0px',
},
+ position: 'absolute',
};
const slideDownSettings: PositionAnimationSettings = {
duration: '0.3s',
@@ -59,10 +60,10 @@ export const SlidingPanel: React.StatelessComponent<SlidingPanelProps> = props =
from: '0px',
to: slideAmount,
},
+ position: 'absolute',
};
return (
<SlideAnimation
- position="absolute"
slideInSettings={slideUpSettings}
slideOutSettings={slideDownSettings}
animationState={animationState}
diff --git a/packages/instant/src/components/timed_progress_bar.tsx b/packages/instant/src/components/timed_progress_bar.tsx
index f2a6f5745..59aaa33a1 100644
--- a/packages/instant/src/components/timed_progress_bar.tsx
+++ b/packages/instant/src/components/timed_progress_bar.tsx
@@ -70,9 +70,11 @@ export const TimedProgress =
styled.div <
TimedProgressProps >
`
- background-color: ${props => props.theme[ColorOption.primaryColor]};
- border-radius: 6px;
- height: 6px;
- animation: ${props => expandingWidthKeyframes(props.fromWidth, props.toWidth)}
- ${props => props.timeMs}ms linear 1 forwards;
- `;
+ && {
+ background-color: ${props => props.theme[ColorOption.primaryColor]};
+ border-radius: 6px;
+ height: 6px;
+ animation: ${props => expandingWidthKeyframes(props.fromWidth, props.toWidth)}
+ ${props => props.timeMs}ms linear 1 forwards;
+ }
+`;
diff --git a/packages/instant/src/components/ui/button.tsx b/packages/instant/src/components/ui/button.tsx
index 5274d835b..b90221bf4 100644
--- a/packages/instant/src/components/ui/button.tsx
+++ b/packages/instant/src/components/ui/button.tsx
@@ -6,6 +6,8 @@ import { ColorOption, styled } from '../../style/theme';
export interface ButtonProps {
backgroundColor?: ColorOption;
borderColor?: ColorOption;
+ fontColor?: ColorOption;
+ fontSize?: string;
width?: string;
padding?: string;
type?: string;
@@ -24,29 +26,39 @@ const darkenOnHoverAmount = 0.1;
const darkenOnActiveAmount = 0.2;
const saturateOnFocusAmount = 0.2;
export const Button = styled(PlainButton)`
- cursor: ${props => (props.isDisabled ? 'default' : 'pointer')};
- transition: background-color, opacity 0.5s ease;
- padding: ${props => props.padding};
- border-radius: 3px;
- outline: none;
- width: ${props => props.width};
- background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
- border: ${props => (props.borderColor ? `1px solid ${props.theme[props.borderColor]}` : 'none')};
- &:hover {
- background-color: ${props =>
- !props.isDisabled
- ? darken(darkenOnHoverAmount, props.theme[props.backgroundColor || 'white'])
- : ''} !important;
- }
- &:active {
- background-color: ${props =>
- !props.isDisabled ? darken(darkenOnActiveAmount, props.theme[props.backgroundColor || 'white']) : ''};
- }
- &:disabled {
- opacity: 0.5;
- }
- &:focus {
- background-color: ${props => saturate(saturateOnFocusAmount, props.theme[props.backgroundColor || 'white'])};
+ && {
+ all: initial;
+ box-sizing: border-box;
+ font-size: ${props => props.fontSize};
+ font-family: 'Inter UI', sans-serif;
+ font-weight: 600;
+ color: ${props => props.fontColor && props.theme[props.fontColor]};
+ cursor: ${props => (props.isDisabled ? 'default' : 'pointer')};
+ transition: background-color, opacity 0.5s ease;
+ padding: ${props => props.padding};
+ border-radius: 3px;
+ text-align: center;
+ outline: none;
+ width: ${props => props.width};
+ background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
+ border: ${props => (props.borderColor ? `1px solid ${props.theme[props.borderColor]}` : 'none')};
+ &:hover {
+ background-color: ${props =>
+ !props.isDisabled
+ ? darken(darkenOnHoverAmount, props.theme[props.backgroundColor || 'white'])
+ : ''} !important;
+ }
+ &:active {
+ background-color: ${props =>
+ !props.isDisabled ? darken(darkenOnActiveAmount, props.theme[props.backgroundColor || 'white']) : ''};
+ }
+ &:disabled {
+ opacity: 0.5;
+ }
+ &:focus {
+ background-color: ${props =>
+ saturate(saturateOnFocusAmount, props.theme[props.backgroundColor || 'white'])};
+ }
}
`;
@@ -55,7 +67,8 @@ Button.defaultProps = {
borderColor: ColorOption.primaryColor,
width: 'auto',
isDisabled: false,
- padding: '1em 2.2em',
+ padding: '.6em 1.2em',
+ fontSize: '15px',
};
Button.displayName = 'Button';
diff --git a/packages/instant/src/components/ui/circle.tsx b/packages/instant/src/components/ui/circle.tsx
index eec2777d2..26764ec71 100644
--- a/packages/instant/src/components/ui/circle.tsx
+++ b/packages/instant/src/components/ui/circle.tsx
@@ -9,10 +9,12 @@ export const Circle =
styled.div <
CircleProps >
`
- width: ${props => props.diameter}px;
- height: ${props => props.diameter}px;
- background-color: ${props => props.fillColor};
- border-radius: 50%;
+ && {
+ width: ${props => props.diameter}px;
+ height: ${props => props.diameter}px;
+ background-color: ${props => props.fillColor};
+ border-radius: 50%;
+ }
`;
Circle.displayName = 'Circle';
diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx
index 403751210..c42082ed5 100644
--- a/packages/instant/src/components/ui/container.tsx
+++ b/packages/instant/src/components/ui/container.tsx
@@ -41,42 +41,45 @@ export const Container =
styled.div <
ContainerProps >
`
- box-sizing: border-box;
- ${props => cssRuleIfExists(props, 'flex-grow')}
- ${props => cssRuleIfExists(props, 'position')}
- ${props => cssRuleIfExists(props, 'top')}
- ${props => cssRuleIfExists(props, 'right')}
- ${props => cssRuleIfExists(props, 'bottom')}
- ${props => cssRuleIfExists(props, 'left')}
- ${props => cssRuleIfExists(props, 'max-width')}
- ${props => cssRuleIfExists(props, 'margin')}
- ${props => cssRuleIfExists(props, 'margin-top')}
- ${props => cssRuleIfExists(props, 'margin-right')}
- ${props => cssRuleIfExists(props, 'margin-bottom')}
- ${props => cssRuleIfExists(props, 'margin-left')}
- ${props => cssRuleIfExists(props, 'padding')}
- ${props => cssRuleIfExists(props, 'border-radius')}
- ${props => cssRuleIfExists(props, 'border')}
- ${props => cssRuleIfExists(props, 'border-top')}
- ${props => cssRuleIfExists(props, 'border-bottom')}
- ${props => cssRuleIfExists(props, 'z-index')}
- ${props => cssRuleIfExists(props, 'white-space')}
- ${props => cssRuleIfExists(props, 'opacity')}
- ${props => cssRuleIfExists(props, 'cursor')}
- ${props => cssRuleIfExists(props, 'overflow')}
- ${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')};
- ${props => props.display && stylesForMedia('display', props.display)}
- ${props => stylesForMedia('width', props.width || 'auto')}
- ${props => stylesForMedia('height', props.height || 'auto')}
- background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
- border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')};
- &:hover {
- ${props =>
- props.darkenOnHover
- ? `background-color: ${
- props.backgroundColor ? darken(0.05, props.theme[props.backgroundColor]) : 'none'
- }`
- : ''};
+ && {
+ all: initial;
+ box-sizing: border-box;
+ ${props => cssRuleIfExists(props, 'flex-grow')}
+ ${props => cssRuleIfExists(props, 'position')}
+ ${props => cssRuleIfExists(props, 'top')}
+ ${props => cssRuleIfExists(props, 'right')}
+ ${props => cssRuleIfExists(props, 'bottom')}
+ ${props => cssRuleIfExists(props, 'left')}
+ ${props => cssRuleIfExists(props, 'max-width')}
+ ${props => cssRuleIfExists(props, 'margin')}
+ ${props => cssRuleIfExists(props, 'margin-top')}
+ ${props => cssRuleIfExists(props, 'margin-right')}
+ ${props => cssRuleIfExists(props, 'margin-bottom')}
+ ${props => cssRuleIfExists(props, 'margin-left')}
+ ${props => cssRuleIfExists(props, 'padding')}
+ ${props => cssRuleIfExists(props, 'border-radius')}
+ ${props => cssRuleIfExists(props, 'border')}
+ ${props => cssRuleIfExists(props, 'border-top')}
+ ${props => cssRuleIfExists(props, 'border-bottom')}
+ ${props => cssRuleIfExists(props, 'z-index')}
+ ${props => cssRuleIfExists(props, 'white-space')}
+ ${props => cssRuleIfExists(props, 'opacity')}
+ ${props => cssRuleIfExists(props, 'cursor')}
+ ${props => cssRuleIfExists(props, 'overflow')}
+ ${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')};
+ ${props => props.display && stylesForMedia<string>('display', props.display)}
+ ${props => props.width && stylesForMedia<string>('width', props.width)}
+ ${props => props.height && stylesForMedia<string>('height', props.height)}
+ background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
+ border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')};
+ &:hover {
+ ${props =>
+ props.darkenOnHover
+ ? `background-color: ${
+ props.backgroundColor ? darken(0.05, props.theme[props.backgroundColor]) : 'none'
+ }`
+ : ''};
+ }
}
`;
diff --git a/packages/instant/src/components/ui/flex.tsx b/packages/instant/src/components/ui/flex.tsx
index fd218b0cd..5b00138b8 100644
--- a/packages/instant/src/components/ui/flex.tsx
+++ b/packages/instant/src/components/ui/flex.tsx
@@ -18,15 +18,18 @@ export const Flex =
styled.div <
FlexProps >
`
- display: ${props => (props.inline ? 'inline-flex' : 'flex')};
- flex-direction: ${props => props.direction};
- flex-wrap: ${props => props.flexWrap};
- ${props => cssRuleIfExists(props, 'flexGrow')}
- justify-content: ${props => props.justify};
- align-items: ${props => props.align};
- background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
- ${props => stylesForMedia('width', props.width || 'auto')}
- ${props => stylesForMedia('height', props.height || 'auto')}
+ && {
+ all: initial;
+ display: ${props => (props.inline ? 'inline-flex' : 'flex')};
+ flex-direction: ${props => props.direction};
+ flex-wrap: ${props => props.flexWrap};
+ ${props => cssRuleIfExists(props, 'flexGrow')}
+ justify-content: ${props => props.justify};
+ align-items: ${props => props.align};
+ background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
+ ${props => (props.width ? stylesForMedia('width', props.width) : '')}
+ ${props => (props.height ? stylesForMedia('height', props.height) : '')}
+ }
`;
Flex.defaultProps = {
diff --git a/packages/instant/src/components/ui/icon.tsx b/packages/instant/src/components/ui/icon.tsx
index 94ea26900..2679dad1a 100644
--- a/packages/instant/src/components/ui/icon.tsx
+++ b/packages/instant/src/components/ui/icon.tsx
@@ -101,15 +101,17 @@ const PlainIcon: React.StatelessComponent<IconProps> = props => {
};
export const Icon = withTheme(styled(PlainIcon)`
- cursor: ${props => (!_.isUndefined(props.onClick) ? 'pointer' : 'default')};
- transition: opacity 0.5s ease;
- padding: ${props => props.padding};
- opacity: ${props => (!_.isUndefined(props.onClick) ? 0.7 : 1)};
- &:hover {
- opacity: 1;
- }
- &:active {
- opacity: 1;
+ && {
+ cursor: ${props => (!_.isUndefined(props.onClick) ? 'pointer' : 'default')};
+ transition: opacity 0.5s ease;
+ padding: ${props => props.padding};
+ opacity: ${props => (!_.isUndefined(props.onClick) ? 0.7 : 1)};
+ &:hover {
+ opacity: 1;
+ }
+ &:active {
+ opacity: 1;
+ }
}
`);
diff --git a/packages/instant/src/components/ui/input.tsx b/packages/instant/src/components/ui/input.tsx
index a884ff7cb..2fb408db4 100644
--- a/packages/instant/src/components/ui/input.tsx
+++ b/packages/instant/src/components/ui/input.tsx
@@ -16,17 +16,20 @@ export const Input =
styled.input <
InputProps >
`
- font-size: ${props => props.fontSize};
- width: ${props => props.width};
- padding: 0.1em 0em;
- font-family: 'Inter UI';
- color: ${props => props.theme[props.fontColor || 'white']};
- background: transparent;
- outline: none;
- border: none;
- &::placeholder {
+ && {
+ all: initial;
+ font-size: ${props => props.fontSize};
+ width: ${props => props.width};
+ padding: 0.1em 0em;
+ font-family: 'Inter UI';
color: ${props => props.theme[props.fontColor || 'white']};
- opacity: 0.5;
+ background: transparent;
+ outline: none;
+ border: none;
+ &::placeholder {
+ color: ${props => props.theme[props.fontColor || 'white']};
+ opacity: 0.5;
+ }
}
`;
diff --git a/packages/instant/src/components/ui/overlay.tsx b/packages/instant/src/components/ui/overlay.tsx
index 7110ee70f..8c9572615 100644
--- a/packages/instant/src/components/ui/overlay.tsx
+++ b/packages/instant/src/components/ui/overlay.tsx
@@ -24,13 +24,15 @@ const PlainOverlay: React.StatelessComponent<OverlayProps> = ({ children, classN
</Flex>
);
export const Overlay = styled(PlainOverlay)`
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: ${props => props.zIndex}
- background-color: ${overlayBlack};
+ && {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: ${props => props.zIndex}
+ background-color: ${overlayBlack};
+ }
`;
Overlay.defaultProps = {
diff --git a/packages/instant/src/components/ui/text.tsx b/packages/instant/src/components/ui/text.tsx
index cba6e7b20..c6a76ff18 100644
--- a/packages/instant/src/components/ui/text.tsx
+++ b/packages/instant/src/components/ui/text.tsx
@@ -27,25 +27,28 @@ export const Text =
styled.div <
TextProps >
`
- font-family: ${props => props.fontFamily};
- font-style: ${props => props.fontStyle};
- font-weight: ${props => props.fontWeight};
- font-size: ${props => props.fontSize};
- opacity: ${props => props.opacity};
- text-decoration-line: ${props => props.textDecorationLine};
- ${props => (props.lineHeight ? `line-height: ${props.lineHeight}` : '')};
- ${props => (props.center ? 'text-align: center' : '')};
- color: ${props => props.fontColor && props.theme[props.fontColor]};
- ${props => (props.minHeight ? `min-height: ${props.minHeight}` : '')};
- ${props => (props.onClick ? 'cursor: pointer' : '')};
- transition: color 0.5s ease;
- ${props => (props.noWrap ? 'white-space: nowrap' : '')};
- ${props => (props.display ? `display: ${props.display}` : '')};
- ${props => (props.letterSpacing ? `letter-spacing: ${props.letterSpacing}` : '')};
- ${props => (props.textTransform ? `text-transform: ${props.textTransform}` : '')};
- &:hover {
- ${props =>
- props.onClick ? `color: ${darken(darkenOnHoverAmount, props.theme[props.fontColor || 'white'])}` : ''};
+ && {
+ all: initial;
+ font-family: 'Inter UI', sans-serif;
+ font-style: ${props => props.fontStyle};
+ font-weight: ${props => props.fontWeight};
+ font-size: ${props => props.fontSize};
+ opacity: ${props => props.opacity};
+ text-decoration-line: ${props => props.textDecorationLine};
+ ${props => (props.lineHeight ? `line-height: ${props.lineHeight}` : '')};
+ ${props => (props.center ? 'text-align: center' : '')};
+ color: ${props => props.fontColor && props.theme[props.fontColor]};
+ ${props => (props.minHeight ? `min-height: ${props.minHeight}` : '')};
+ ${props => (props.onClick ? 'cursor: pointer' : '')};
+ transition: color 0.5s ease;
+ ${props => (props.noWrap ? 'white-space: nowrap' : '')};
+ ${props => (props.display ? `display: ${props.display}` : '')};
+ ${props => (props.letterSpacing ? `letter-spacing: ${props.letterSpacing}` : '')};
+ ${props => (props.textTransform ? `text-transform: ${props.textTransform}` : '')};
+ &:hover {
+ ${props =>
+ props.onClick ? `color: ${darken(darkenOnHoverAmount, props.theme[props.fontColor || 'white'])}` : ''};
+ }
}
`;
@@ -61,14 +64,3 @@ Text.defaultProps = {
};
Text.displayName = 'Text';
-
-export const Title: React.StatelessComponent<TextProps> = props => <Text {...props} />;
-
-Title.defaultProps = {
- fontSize: '20px',
- fontWeight: 600,
- opacity: 1,
- fontColor: ColorOption.primaryColor,
-};
-
-Title.displayName = 'Title';
diff --git a/packages/instant/src/components/zero_ex_instant.tsx b/packages/instant/src/components/zero_ex_instant.tsx
index 907c42e7a..b945f9908 100644
--- a/packages/instant/src/components/zero_ex_instant.tsx
+++ b/packages/instant/src/components/zero_ex_instant.tsx
@@ -1,5 +1,7 @@
import * as React from 'react';
+import { INJECTED_DIV_CLASS } from '../constants';
+
import { ZeroExInstantContainer } from './zero_ex_instant_container';
import { ZeroExInstantProvider, ZeroExInstantProviderProps } from './zero_ex_instant_provider';
@@ -7,8 +9,10 @@ export type ZeroExInstantProps = ZeroExInstantProviderProps;
export const ZeroExInstant: React.StatelessComponent<ZeroExInstantProps> = props => {
return (
- <ZeroExInstantProvider {...props}>
- <ZeroExInstantContainer />
- </ZeroExInstantProvider>
+ <div className={INJECTED_DIV_CLASS}>
+ <ZeroExInstantProvider {...props}>
+ <ZeroExInstantContainer />
+ </ZeroExInstantProvider>
+ </div>
);
};
diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx
index ef6adf384..d2216b54f 100644
--- a/packages/instant/src/components/zero_ex_instant_container.tsx
+++ b/packages/instant/src/components/zero_ex_instant_container.tsx
@@ -12,6 +12,7 @@ import { ColorOption } from '../style/theme';
import { zIndex } from '../style/z_index';
import { SlideAnimationState } from './animations/slide_animation';
+import { CSSReset } from './css_reset';
import { SlidingPanel } from './sliding_panel';
import { Container } from './ui/container';
import { Flex } from './ui/flex';
@@ -27,40 +28,43 @@ export class ZeroExInstantContainer extends React.Component<ZeroExInstantContain
};
public render(): React.ReactNode {
return (
- <Container
- width={{ default: '350px', sm: '100%' }}
- height={{ default: 'auto', sm: '100%' }}
- position="relative"
- >
- <Container zIndex={zIndex.errorPopup} position="relative">
- <LatestError />
- </Container>
+ <React.Fragment>
+ <CSSReset />
<Container
- zIndex={zIndex.mainContainer}
+ width={{ default: '350px', sm: '100%' }}
+ height={{ default: 'auto', sm: '100%' }}
position="relative"
- backgroundColor={ColorOption.white}
- borderRadius="3px"
- hasBoxShadow={true}
- overflow="hidden"
- height="100%"
>
- <Flex direction="column" height="100%" justify="flex-start">
- <SelectedAssetInstantHeading onSelectAssetClick={this._handleSymbolClick} />
- <SelectedAssetBuyOrderProgress />
- <LatestBuyQuoteOrderDetails />
- <Container padding="20px" width="100%">
- <SelectedAssetBuyOrderStateButtons />
- </Container>
- </Flex>
- <SlidingPanel
- title="Select Token"
- animationState={this.state.tokenSelectionPanelAnimationState}
- onClose={this._handlePanelClose}
+ <Container position="relative">
+ <LatestError />
+ </Container>
+ <Container
+ zIndex={zIndex.mainContainer}
+ position="relative"
+ backgroundColor={ColorOption.white}
+ borderRadius="3px"
+ hasBoxShadow={true}
+ overflow="hidden"
+ height="100%"
>
- <AvailableERC20TokenSelector onTokenSelect={this._handlePanelClose} />
- </SlidingPanel>
+ <Flex direction="column" justify="flex-start" height="100%">
+ <SelectedAssetInstantHeading onSelectAssetClick={this._handleSymbolClick} />
+ <SelectedAssetBuyOrderProgress />
+ <LatestBuyQuoteOrderDetails />
+ <Container padding="20px" width="100%">
+ <SelectedAssetBuyOrderStateButtons />
+ </Container>
+ </Flex>
+ <SlidingPanel
+ title="Select Token"
+ animationState={this.state.tokenSelectionPanelAnimationState}
+ onClose={this._handlePanelClose}
+ >
+ <AvailableERC20TokenSelector onTokenSelect={this._handlePanelClose} />
+ </SlidingPanel>
+ </Container>
</Container>
- </Container>
+ </React.Fragment>
);
}
private readonly _handleSymbolClick = (): void => {
diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx
index fa0588b71..cceb44377 100644
--- a/packages/instant/src/components/zero_ex_instant_provider.tsx
+++ b/packages/instant/src/components/zero_ex_instant_provider.tsx
@@ -94,6 +94,8 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
}
// tslint:disable-next-line:no-floating-promises
asyncData.fetchAccountInfoAndDispatchToStore(this._store);
+ // tslint:disable-next-line:no-floating-promises
+ asyncData.fetchCurrentBuyQuoteAndDispatchToStore(this._store);
// warm up the gas price estimator cache just in case we can't
// grab the gas price estimate when submitting the transaction
// tslint:disable-next-line:no-floating-promises
diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts
index 705313ce7..b5c4f96e4 100644
--- a/packages/instant/src/constants.ts
+++ b/packages/instant/src/constants.ts
@@ -5,6 +5,7 @@ import { AccountNotReady, AccountState, Network } from './types';
export const BIG_NUMBER_ZERO = new BigNumber(0);
export const ETH_DECIMALS = 18;
export const DEFAULT_ZERO_EX_CONTAINER_SELECTOR = '#zeroExInstantContainer';
+export const INJECTED_DIV_CLASS = 'zeroExInstantResetRoot';
export const INJECTED_DIV_ID = 'zeroExInstant';
export const WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX = 'Transaction failed';
export const GWEI_IN_WEI = new BigNumber(1000000000);
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..c550aef04 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 { buyQuoteUpdater } from '../util/buy_quote_updater';
export interface SelectedERC20AssetAmountInputProps {
fontColor?: ColorOption;
@@ -70,52 +67,9 @@ const mapStateToProps = (state: State, _ownProps: SelectedERC20AssetAmountInputP
};
};
-const updateBuyQuoteAsync = async (
- assetBuyer: AssetBuyer,
- dispatch: Dispatch<Action>,
- asset: ERC20Asset,
- assetAmount: BigNumber,
- affiliateInfo?: AffiliateInfo,
-): Promise<void> => {
- // 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));
-};
-
-const debouncedUpdateBuyQuoteAsync = _.debounce(updateBuyQuoteAsync, 200, { trailing: true });
+const debouncedUpdateBuyQuoteAsync = _.debounce(buyQuoteUpdater.updateBuyQuoteAsync.bind(buyQuoteUpdater), 200, {
+ trailing: true,
+});
const mapDispatchToProps = (
dispatch: Dispatch<Action>,
diff --git a/packages/instant/src/index.umd.ts b/packages/instant/src/index.umd.ts
index 59d1e646f..0274db30c 100644
--- a/packages/instant/src/index.umd.ts
+++ b/packages/instant/src/index.umd.ts
@@ -2,7 +2,7 @@ import * as _ from 'lodash';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
-import { DEFAULT_ZERO_EX_CONTAINER_SELECTOR, INJECTED_DIV_ID } from './constants';
+import { DEFAULT_ZERO_EX_CONTAINER_SELECTOR, INJECTED_DIV_CLASS, INJECTED_DIV_ID } from './constants';
import { ZeroExInstantOverlay, ZeroExInstantOverlayProps } from './index';
import { assert } from './util/assert';
@@ -41,6 +41,7 @@ export const render = (props: ZeroExInstantOverlayProps, selector: string = DEFA
const appendTo = appendToIfExists as Element;
const injectedDiv = document.createElement('div');
injectedDiv.setAttribute('id', INJECTED_DIV_ID);
+ injectedDiv.setAttribute('class', INJECTED_DIV_CLASS);
appendTo.appendChild(injectedDiv);
const instantOverlayProps = {
...props,
diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts
index 862f60e78..bff9d8814 100644
--- a/packages/instant/src/redux/async_data.ts
+++ b/packages/instant/src/redux/async_data.ts
@@ -1,8 +1,10 @@
+import { AssetProxyId } from '@0x/types';
import * as _ from 'lodash';
import { BIG_NUMBER_ZERO } from '../constants';
-import { AccountState } from '../types';
+import { AccountState, ERC20Asset } from '../types';
import { assetUtils } from '../util/asset';
+import { buyQuoteUpdater } from '../util/buy_quote_updater';
import { coinbaseApi } from '../util/coinbase_api';
import { errorFlasher } from '../util/error_flasher';
@@ -70,4 +72,21 @@ export const asyncData = {
return;
}
},
+ fetchCurrentBuyQuoteAndDispatchToStore: async (store: Store) => {
+ const { providerState, selectedAsset, selectedAssetAmount, affiliateInfo } = store.getState();
+ const assetBuyer = providerState.assetBuyer;
+ if (
+ !_.isUndefined(selectedAssetAmount) &&
+ !_.isUndefined(selectedAsset) &&
+ selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20
+ ) {
+ await buyQuoteUpdater.updateBuyQuoteAsync(
+ assetBuyer,
+ store.dispatch,
+ selectedAsset as ERC20Asset,
+ selectedAssetAmount,
+ affiliateInfo,
+ );
+ }
+ },
};
diff --git a/packages/instant/src/style/media.ts b/packages/instant/src/style/media.ts
index beabbac46..5e7aaba37 100644
--- a/packages/instant/src/style/media.ts
+++ b/packages/instant/src/style/media.ts
@@ -14,30 +14,38 @@ const generateMediaWrapper = (screenWidth: ScreenWidths) => (...args: any[]) =>
}
`;
-const media = {
+export const media = {
small: generateMediaWrapper(ScreenWidths.Sm),
medium: generateMediaWrapper(ScreenWidths.Md),
large: generateMediaWrapper(ScreenWidths.Lg),
};
-export interface ScreenSpecifications {
- default: string;
- sm?: string;
- md?: string;
- lg?: string;
+export interface ScreenSpecification<T> {
+ default: T;
+ sm?: T;
+ md?: T;
+ lg?: T;
}
-export type MediaChoice = string | ScreenSpecifications;
-export const stylesForMedia = (cssPropertyName: string, choice: MediaChoice): InterpolationValue[] => {
- if (typeof choice === 'string') {
+export type OptionallyScreenSpecific<T> = T | ScreenSpecification<T>;
+export type MediaChoice = OptionallyScreenSpecific<string>;
+/**
+ * Given a css property name and a OptionallyScreenSpecific value,
+ * generates css properties with screen-specific viewport styling
+ */
+export function stylesForMedia<T extends string | number>(
+ cssPropertyName: string,
+ choice: OptionallyScreenSpecific<T>,
+): InterpolationValue[] {
+ if (typeof choice === 'object') {
return css`
- ${cssPropertyName}: ${choice};
- `;
- }
-
- return css`
${cssPropertyName}: ${choice.default};
${choice.lg && media.large`${cssPropertyName}: ${choice.lg}`}
${choice.md && media.medium`${cssPropertyName}: ${choice.md}`}
${choice.sm && media.small`${cssPropertyName}: ${choice.sm}`}
`;
-};
+ } else {
+ return css`
+ ${cssPropertyName}: ${choice};
+ `;
+ }
+}
diff --git a/packages/instant/src/style/theme.ts b/packages/instant/src/style/theme.ts
index d10c9b72c..8dada2d28 100644
--- a/packages/instant/src/style/theme.ts
+++ b/packages/instant/src/style/theme.ts
@@ -1,6 +1,6 @@
import * as styledComponents from 'styled-components';
-const { default: styled, css, keyframes, withTheme, ThemeProvider } = styledComponents;
+const { default: styled, css, keyframes, withTheme, createGlobalStyle, ThemeProvider } = styledComponents;
export type Theme = { [key in ColorOption]: string };
@@ -33,4 +33,4 @@ export const theme: Theme = {
export const transparentWhite = 'rgba(255,255,255,0.3)';
export const overlayBlack = 'rgba(0, 0, 0, 0.6)';
-export { styled, css, keyframes, withTheme, ThemeProvider };
+export { styled, css, keyframes, withTheme, createGlobalStyle, ThemeProvider };
diff --git a/packages/instant/src/style/z_index.ts b/packages/instant/src/style/z_index.ts
index 727a5189d..03623f044 100644
--- a/packages/instant/src/style/z_index.ts
+++ b/packages/instant/src/style/z_index.ts
@@ -1,5 +1,6 @@
export const zIndex = {
- errorPopup: 1,
+ errorPopBehind: 1,
mainContainer: 2,
panel: 3,
+ errorPopUp: 4,
};
diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts
new file mode 100644
index 000000000..e697d3ef7
--- /dev/null
+++ b/packages/instant/src/util/buy_quote_updater.ts
@@ -0,0 +1,56 @@
+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 '../util/error_flasher';
+
+export const buyQuoteUpdater = {
+ updateBuyQuoteAsync: async (
+ assetBuyer: AssetBuyer,
+ dispatch: Dispatch<Action>,
+ asset: ERC20Asset,
+ assetAmount: BigNumber,
+ affiliateInfo?: AffiliateInfo,
+ ): Promise<void> => {
+ // 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));
+ },
+};
diff --git a/packages/sra-spec/src/json-schemas.ts b/packages/sra-spec/src/json-schemas.ts
index 9eb32de59..f9342ca9e 100644
--- a/packages/sra-spec/src/json-schemas.ts
+++ b/packages/sra-spec/src/json-schemas.ts
@@ -2,6 +2,7 @@ import { schemas as jsonSchemas } from '@0x/json-schemas';
// Only include schemas we actually need
const {
+ wholeNumberSchema,
numberSchema,
addressSchema,
hexSchema,
@@ -28,6 +29,7 @@ const {
} = jsonSchemas;
const usedSchemas = {
+ wholeNumberSchema,
numberSchema,
addressSchema,
hexSchema,
diff --git a/packages/utils/src/configured_bignumber.ts b/packages/utils/src/configured_bignumber.ts
index 2b22b6938..34b57d303 100644
--- a/packages/utils/src/configured_bignumber.ts
+++ b/packages/utils/src/configured_bignumber.ts
@@ -11,4 +11,27 @@ BigNumber.config({
DECIMAL_PLACES: 78,
});
+// Set a debug print function for NodeJS
+// Upstream issue: https://github.com/MikeMcl/bignumber.js/issues/188
+import isNode = require('detect-node');
+if (isNode) {
+ // Dynamically load a NodeJS specific module.
+ // Typescript requires all imports to be global, so we need to use
+ // `const` here and disable the tslint warning.
+ // tslint:disable-next-line: no-var-requires
+ const util = require('util');
+
+ // Set a custom util.inspect function
+ // HACK: We add a function to the BigNumber class by assigning to the
+ // prototype. The function name is a symbol provided by Node.
+ (BigNumber.prototype as any)[util.inspect.custom] = function(): string {
+ // HACK: When executed, `this` will refer to the BigNumber instance.
+ // This is also why we need a function expression instead of an
+ // arrow function, as the latter does not have a `this`.
+ // Return the readable string representation
+ // tslint:disable-next-line: no-invalid-this
+ return this.toString();
+ };
+}
+
export { BigNumber };
diff --git a/packages/website/package.json b/packages/website/package.json
index efb97d309..dd14d4b17 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -7,14 +7,15 @@
"private": true,
"description": "Website and 0x portal dapp",
"scripts": {
- "build": "node --max_old_space_size=8192 ../../node_modules/.bin/webpack --mode production",
+ "build": "yarn build:dev",
+ "build:prod": "node --max_old_space_size=8192 ../../node_modules/.bin/webpack --mode production",
"build:dev": "../../node_modules/.bin/webpack --mode development",
"clean": "shx rm -f public/bundle*",
"lint": "tslint --format stylish --project . 'ts/**/*.ts' 'ts/**/*.tsx'",
"dev": "webpack-dev-server --mode development --content-base public --https",
- "deploy_dogfood": "npm run build; aws s3 sync ./public/. s3://dogfood.0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers",
- "deploy_staging": "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers",
- "deploy_live": "DEPLOY_ROLLBAR_SOURCEMAPS=true npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --exclude *.map.js"
+ "deploy_dogfood": "npm run build:prod; aws s3 sync ./public/. s3://dogfood.0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers",
+ "deploy_staging": "npm run build:prod; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers",
+ "deploy_live": "DEPLOY_ROLLBAR_SOURCEMAPS=true npm run build:prod; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --exclude *.map.js"
},
"author": "Fabio Berger",
"license": "Apache-2.0",