aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website
diff options
context:
space:
mode:
Diffstat (limited to 'packages/website')
-rw-r--r--packages/website/package.json4
-rw-r--r--packages/website/public/images/lock_icon.svg3
-rw-r--r--packages/website/public/images/setup_account_icon.svg3
-rw-r--r--packages/website/ts/blockchain.ts2
-rw-r--r--packages/website/ts/blockchain_watcher.ts7
-rw-r--r--packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx2
-rw-r--r--packages/website/ts/components/dialogs/send_dialog.tsx1
-rw-r--r--packages/website/ts/components/inputs/balance_bounded_input.tsx35
-rw-r--r--packages/website/ts/components/inputs/eth_amount_input.tsx4
-rw-r--r--packages/website/ts/components/inputs/token_amount_input.tsx2
-rw-r--r--packages/website/ts/components/portal/portal.tsx12
-rw-r--r--packages/website/ts/components/relayer_index/relayer_grid_tile.tsx2
-rw-r--r--packages/website/ts/components/top_bar/provider_display.tsx118
-rw-r--r--packages/website/ts/components/top_bar/provider_picker.tsx79
-rw-r--r--packages/website/ts/components/top_bar/top_bar.tsx4
-rw-r--r--packages/website/ts/components/ui/container.tsx1
-rw-r--r--packages/website/ts/components/ui/drop_down.tsx52
-rw-r--r--packages/website/ts/components/ui/simple_menu.tsx88
-rw-r--r--packages/website/ts/components/ui/text.tsx5
-rw-r--r--packages/website/ts/components/wallet/wallet.tsx115
-rw-r--r--packages/website/ts/components/wallet/wrap_ether_item.tsx5
-rw-r--r--packages/website/ts/types.ts2
-rw-r--r--packages/website/ts/utils/constants.ts5
-rw-r--r--packages/website/ts/utils/utils.ts21
24 files changed, 285 insertions, 287 deletions
diff --git a/packages/website/package.json b/packages/website/package.json
index c7b689356..1bfc385b5 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -38,6 +38,7 @@
"lodash": "^4.17.4",
"material-ui": "^0.17.1",
"moment": "2.21.0",
+ "numeral": "^2.0.6",
"polished": "^1.9.2",
"query-string": "^6.0.0",
"react": "15.6.1",
@@ -58,7 +59,7 @@
"thenby": "^1.2.3",
"truffle-contract": "2.0.1",
"web3": "^0.20.0",
- "web3-provider-engine": "^14.0.4",
+ "web3-provider-engine": "14.0.6",
"whatwg-fetch": "^2.0.3",
"xml-js": "^1.6.4"
},
@@ -71,6 +72,7 @@
"@types/lodash": "4.14.104",
"@types/material-ui": "0.18.0",
"@types/node": "^8.0.53",
+ "@types/numeral": "^0.0.22",
"@types/query-string": "^5.1.0",
"@types/react": "16.3.13",
"@types/react-copy-to-clipboard": "^4.2.0",
diff --git a/packages/website/public/images/lock_icon.svg b/packages/website/public/images/lock_icon.svg
new file mode 100644
index 000000000..83e8191a1
--- /dev/null
+++ b/packages/website/public/images/lock_icon.svg
@@ -0,0 +1,3 @@
+<svg width="26" height="32" viewBox="0 0 26 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M6.47619 0C3.79509 0 1.60489 2.21216 1.60489 4.92014V6.33135C0.717479 6.33135 -3.60127e-08 7.05602 -3.60127e-08 7.95232V14.379C-3.60127e-08 15.2753 0.717479 16 1.60489 16H11.3475C12.2349 16 12.9524 15.2753 12.9524 14.379V7.95232C12.9524 7.05602 12.2349 6.33135 11.3475 6.33135V4.92014C11.3475 2.21216 9.1573 0 6.47619 0ZM9.6482 6.33135H3.30418V4.92014C3.30418 3.16567 4.72026 1.71633 6.47619 1.71633C8.23213 1.71633 9.6482 3.16567 9.6482 4.92014V6.33135Z" transform="scale(2)" fill="black"/>
+</svg>
diff --git a/packages/website/public/images/setup_account_icon.svg b/packages/website/public/images/setup_account_icon.svg
new file mode 100644
index 000000000..eaa5b2fd6
--- /dev/null
+++ b/packages/website/public/images/setup_account_icon.svg
@@ -0,0 +1,3 @@
+<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.5 9C16.5 13.1421 13.1421 16.5 9 16.5C4.85791 16.5 1.5 13.1421 1.5 9C1.5 4.85791 4.85791 1.5 9 1.5C13.1421 1.5 16.5 4.85791 16.5 9ZM18 9C18 13.9706 13.9707 18 9 18C4.0293 18 0 13.9706 0 9C0 4.02942 4.0293 0 9 0C13.9707 0 18 4.02942 18 9ZM9.21973 5.7196C9.5127 5.42664 9.9873 5.42664 10.2803 5.7196L13.0806 8.51953C13.373 8.8125 13.373 9.28735 13.0806 9.5802L10.2803 12.3802C9.9873 12.6731 9.5127 12.6731 9.21973 12.3802C8.92676 12.0873 8.92676 11.6124 9.21973 11.3196L10.7393 9.7998H4.75C4.33594 9.7998 4 9.46399 4 9.0498C4 8.63562 4.33594 8.2998 4.75 8.2998H10.7393L9.21973 6.78015C8.92676 6.4873 8.92676 6.01245 9.21973 5.7196Z" transform="scale(2)" fill="#3289F1"/>
+</svg>
diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts
index d18c34c32..b59306974 100644
--- a/packages/website/ts/blockchain.ts
+++ b/packages/website/ts/blockchain.ts
@@ -73,6 +73,8 @@ const providerToName: { [provider: string]: string } = {
[Providers.Metamask]: constants.PROVIDER_NAME_METAMASK,
[Providers.Parity]: constants.PROVIDER_NAME_PARITY_SIGNER,
[Providers.Mist]: constants.PROVIDER_NAME_MIST,
+ [Providers.Toshi]: constants.PROVIDER_NAME_TOSHI,
+ [Providers.Cipher]: constants.PROVIDER_NAME_CIPHER,
};
export class Blockchain {
diff --git a/packages/website/ts/blockchain_watcher.ts b/packages/website/ts/blockchain_watcher.ts
index df5f73fd1..4b23aa98a 100644
--- a/packages/website/ts/blockchain_watcher.ts
+++ b/packages/website/ts/blockchain_watcher.ts
@@ -10,6 +10,7 @@ export class BlockchainWatcher {
private _watchBalanceIntervalId: NodeJS.Timer;
private _prevUserEtherBalanceInWei?: BigNumber;
private _prevUserAddressIfExists: string;
+ private _prevNodeVersionIfExists: string;
constructor(dispatcher: Dispatcher, web3Wrapper: Web3Wrapper, shouldPollUserAddress: boolean) {
this._dispatcher = dispatcher;
this._shouldPollUserAddress = shouldPollUserAddress;
@@ -43,11 +44,9 @@ export class BlockchainWatcher {
);
}
private async _updateBalanceAsync(): Promise<void> {
- let prevNodeVersion: string;
- // Check for node version changes
const currentNodeVersion = await this._web3Wrapper.getNodeVersionAsync();
- if (currentNodeVersion !== prevNodeVersion) {
- prevNodeVersion = currentNodeVersion;
+ if (this._prevNodeVersionIfExists !== currentNodeVersion) {
+ this._prevNodeVersionIfExists = currentNodeVersion;
this._dispatcher.updateNodeVersion(currentNodeVersion);
}
diff --git a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
index 9ac78e80e..7b09cc92c 100644
--- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
+++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
@@ -103,7 +103,6 @@ export class EthWethConversionDialog extends React.Component<
shouldCheckAllowance={false}
onChange={this._onValueChange.bind(this)}
amount={this.state.value}
- onVisitBalancesPageClick={this.props.onCancelled}
/>
) : (
<EthAmountInput
@@ -112,7 +111,6 @@ export class EthWethConversionDialog extends React.Component<
onChange={this._onValueChange.bind(this)}
shouldCheckBalance={true}
shouldShowIncompleteErrs={this.state.shouldShowIncompleteErrs}
- onVisitBalancesPageClick={this.props.onCancelled}
/>
)}
<div className="pt1" style={{ fontSize: 12 }}>
diff --git a/packages/website/ts/components/dialogs/send_dialog.tsx b/packages/website/ts/components/dialogs/send_dialog.tsx
index 421f18b4f..8a98fdf69 100644
--- a/packages/website/ts/components/dialogs/send_dialog.tsx
+++ b/packages/website/ts/components/dialogs/send_dialog.tsx
@@ -80,7 +80,6 @@ export class SendDialog extends React.Component<SendDialogProps, SendDialogState
shouldCheckAllowance={false}
onChange={this._onValueChange.bind(this)}
amount={this.state.value}
- onVisitBalancesPageClick={this.props.onCancelled}
lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
/>
</div>
diff --git a/packages/website/ts/components/inputs/balance_bounded_input.tsx b/packages/website/ts/components/inputs/balance_bounded_input.tsx
index 968609030..f23beb436 100644
--- a/packages/website/ts/components/inputs/balance_bounded_input.tsx
+++ b/packages/website/ts/components/inputs/balance_bounded_input.tsx
@@ -3,9 +3,8 @@ import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
import TextField from 'material-ui/TextField';
import * as React from 'react';
-import { Link } from 'react-router-dom';
import { RequiredLabel } from 'ts/components/ui/required_label';
-import { ValidatedBigNumberCallback, WebsitePaths } from 'ts/types';
+import { ValidatedBigNumberCallback } from 'ts/types';
import { utils } from 'ts/utils/utils';
interface BalanceBoundedInputProps {
@@ -18,8 +17,6 @@ interface BalanceBoundedInputProps {
shouldShowIncompleteErrs?: boolean;
shouldCheckBalance: boolean;
validate?: (amount: BigNumber) => React.ReactNode;
- onVisitBalancesPageClick?: () => void;
- shouldHideVisitBalancesLink?: boolean;
isDisabled?: boolean;
shouldShowErrs?: boolean;
shouldShowUnderline?: boolean;
@@ -35,7 +32,6 @@ interface BalanceBoundedInputState {
export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProps, BalanceBoundedInputState> {
public static defaultProps: Partial<BalanceBoundedInputProps> = {
shouldShowIncompleteErrs: false,
- shouldHideVisitBalancesLink: false,
isDisabled: false,
shouldShowErrs: true,
hintText: 'amount',
@@ -124,38 +120,11 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
return 'Cannot be zero';
}
if (this.props.shouldCheckBalance && amount.gt(balance)) {
- return <span>Insufficient balance. {this._renderIncreaseBalanceLink()}</span>;
+ return <span>Insufficient balance.</span>;
}
const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount);
return errMsg;
}
- private _renderIncreaseBalanceLink(): React.ReactNode {
- if (this.props.shouldHideVisitBalancesLink) {
- return null;
- }
-
- const increaseBalanceText = 'Increase balance';
- const linkStyle = {
- cursor: 'pointer',
- color: colors.darkestGrey,
- textDecoration: 'underline',
- display: 'inline',
- };
- if (_.isUndefined(this.props.onVisitBalancesPageClick)) {
- return (
- <Link to={`${WebsitePaths.Portal}/balances`} style={linkStyle}>
- {increaseBalanceText}
- </Link>
- );
- } else {
- return (
- <div onClick={this.props.onVisitBalancesPageClick} style={linkStyle}>
- {increaseBalanceText}
- </div>
- );
- }
- }
-
private _setAmountState(amount: string, balance: BigNumber, callback: () => void = _.noop): void {
const errorMsg = this._validate(amount, balance);
this.props.onErrorMsgChange(errorMsg);
diff --git a/packages/website/ts/components/inputs/eth_amount_input.tsx b/packages/website/ts/components/inputs/eth_amount_input.tsx
index 1f0f27410..552d4277a 100644
--- a/packages/website/ts/components/inputs/eth_amount_input.tsx
+++ b/packages/website/ts/components/inputs/eth_amount_input.tsx
@@ -14,9 +14,7 @@ interface EthAmountInputProps {
onChange: ValidatedBigNumberCallback;
onErrorMsgChange?: (errorMsg: React.ReactNode) => void;
shouldShowIncompleteErrs: boolean;
- onVisitBalancesPageClick?: () => void;
shouldCheckBalance: boolean;
- shouldHideVisitBalancesLink?: boolean;
shouldShowErrs?: boolean;
shouldShowUnderline?: boolean;
style?: React.CSSProperties;
@@ -46,8 +44,6 @@ export class EthAmountInput extends React.Component<EthAmountInputProps, EthAmou
onErrorMsgChange={this.props.onErrorMsgChange}
shouldCheckBalance={this.props.shouldCheckBalance}
shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs}
- onVisitBalancesPageClick={this.props.onVisitBalancesPageClick}
- shouldHideVisitBalancesLink={this.props.shouldHideVisitBalancesLink}
hintText={this.props.hintText}
shouldShowErrs={this.props.shouldShowErrs}
shouldShowUnderline={this.props.shouldShowUnderline}
diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx
index a67120320..93ef516cf 100644
--- a/packages/website/ts/components/inputs/token_amount_input.tsx
+++ b/packages/website/ts/components/inputs/token_amount_input.tsx
@@ -21,7 +21,6 @@ interface TokenAmountInputProps {
shouldCheckAllowance: boolean;
onChange: ValidatedBigNumberCallback;
onErrorMsgChange?: (errorMsg: React.ReactNode) => void;
- onVisitBalancesPageClick?: () => void;
lastForceTokenStateRefetch: number;
shouldShowErrs?: boolean;
shouldShowUnderline?: boolean;
@@ -88,7 +87,6 @@ export class TokenAmountInput extends React.Component<TokenAmountInputProps, Tok
validate={this._validate.bind(this)}
shouldCheckBalance={this.props.shouldCheckBalance}
shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs}
- onVisitBalancesPageClick={this.props.onVisitBalancesPageClick}
isDisabled={!this.state.isBalanceAndAllowanceLoaded}
hintText={this.props.hintText}
shouldShowErrs={this.props.shouldShowErrs}
diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx
index 9c0cb866d..8c3b5cfd7 100644
--- a/packages/website/ts/components/portal/portal.tsx
+++ b/packages/website/ts/components/portal/portal.tsx
@@ -1,7 +1,6 @@
import { colors, constants as sharedConstants } from '@0xproject/react-shared';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
-import Help from 'material-ui/svg-icons/action/help';
import * as React from 'react';
import * as DocumentTitle from 'react-document-title';
import { Link, Route, RouteComponentProps, Switch } from 'react-router-dom';
@@ -24,6 +23,7 @@ import { TopBar, TopBarDisplayType } from 'ts/components/top_bar/top_bar';
import { TradeHistory } from 'ts/components/trade_history/trade_history';
import { Container } from 'ts/components/ui/container';
import { FlashMessage } from 'ts/components/ui/flash_message';
+import { Image } from 'ts/components/ui/image';
import { Text } from 'ts/components/ui/text';
import { Wallet } from 'ts/components/wallet/wallet';
import { GenerateOrderForm } from 'ts/containers/generate_order_form';
@@ -368,11 +368,11 @@ export class Portal extends React.Component<PortalProps, PortalState> {
const shouldStartOnboarding = !isMobile || this.props.location.pathname === `${WebsitePaths.Portal}/account`;
const startOnboarding = (
<Container className="flex items-center center">
- <Help style={{ width: '20px', height: '20px' }} color={colors.mediumBlue} />
- <Container marginLeft="8px">
- <Text fontColor={colors.mediumBlue} fontSize="16px" onClick={this._startOnboarding.bind(this)}>
- Learn how to set up your account
- </Text>
+ <Text fontColor={colors.mediumBlue} fontSize="16px" onClick={this._startOnboarding.bind(this)}>
+ Set up your account to start trading
+ </Text>
+ <Container marginLeft="8px" paddingTop="3px">
+ <Image src="/images/setup_account_icon.svg" height="20px" width="20x" />
</Container>
</Container>
);
diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx
index 20594e5ca..02bc1b014 100644
--- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx
+++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx
@@ -56,7 +56,7 @@ const styles: Styles = {
};
const FALLBACK_IMG_SRC = '/images/relayer_fallback.png';
-const FALLBACK_PRIMARY_COLOR = colors.grey200;
+const FALLBACK_PRIMARY_COLOR = colors.grey300;
const NO_CONTENT_MESSAGE = '--';
const RELAYER_ICON_HEIGHT = '110px';
diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx
index 8743e4320..806eaeea5 100644
--- a/packages/website/ts/components/top_bar/provider_display.tsx
+++ b/packages/website/ts/components/top_bar/provider_display.tsx
@@ -1,23 +1,26 @@
import { Styles } from '@0xproject/react-shared';
import * as _ from 'lodash';
import CircularProgress from 'material-ui/CircularProgress';
-import RaisedButton from 'material-ui/RaisedButton';
import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
-import Lock from 'material-ui/svg-icons/action/lock';
import * as React from 'react';
import { Blockchain } from 'ts/blockchain';
-import { ProviderPicker } from 'ts/components/top_bar/provider_picker';
import { AccountConnection } from 'ts/components/ui/account_connection';
import { Container } from 'ts/components/ui/container';
import { DropDown } from 'ts/components/ui/drop_down';
import { Identicon } from 'ts/components/ui/identicon';
+import { Image } from 'ts/components/ui/image';
import { Island } from 'ts/components/ui/island';
+import {
+ CopyAddressSimpleMenuItem,
+ DifferentWalletSimpleMenuItem,
+ GoToAccountManagementSimpleMenuItem,
+ SimpleMenu,
+} from 'ts/components/ui/simple_menu';
import { Text } from 'ts/components/ui/text';
import { Dispatcher } from 'ts/redux/dispatcher';
import { colors } from 'ts/style/colors';
import { AccountState, ProviderType } from 'ts/types';
-import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
const ROOT_HEIGHT = 24;
@@ -44,11 +47,7 @@ const styles: Styles = {
export class ProviderDisplay extends React.Component<ProviderDisplayProps, ProviderDisplayState> {
public render(): React.ReactNode {
- const isExternallyInjectedProvider = utils.isExternallyInjected(
- this.props.providerType,
- this.props.injectedProviderName,
- );
- const hoverActiveNode = (
+ const activeNode = (
<Island className="flex items-center py1 px2" style={styles.root}>
{this._renderIcon()}
<Container marginLeft="12px" marginRight="12px">
@@ -57,93 +56,34 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
{this._renderInjectedProvider()}
</Island>
);
- const hasLedgerProvider = this.props.providerType === ProviderType.Ledger;
- const horizontalPosition = isExternallyInjectedProvider || hasLedgerProvider ? 'left' : 'middle';
return (
<div style={{ width: 'fit-content', height: 48, float: 'right' }}>
<DropDown
- hoverActiveNode={hoverActiveNode}
- popoverContent={this.renderPopoverContent(isExternallyInjectedProvider, hasLedgerProvider)}
- anchorOrigin={{ horizontal: horizontalPosition, vertical: 'bottom' }}
- targetOrigin={{ horizontal: horizontalPosition, vertical: 'top' }}
+ activeNode={activeNode}
+ popoverContent={this._renderPopoverContent()}
+ anchorOrigin={{ horizontal: 'middle', vertical: 'bottom' }}
+ targetOrigin={{ horizontal: 'middle', vertical: 'top' }}
zDepth={1}
/>
</div>
);
}
- public renderPopoverContent(hasInjectedProvider: boolean, hasLedgerProvider: boolean): React.ReactNode {
- if (!this._isBlockchainReady()) {
- return null;
- } else if (hasInjectedProvider || hasLedgerProvider) {
- return (
- <ProviderPicker
- dispatcher={this.props.dispatcher}
- networkId={this.props.networkId}
- injectedProviderName={this.props.injectedProviderName}
- providerType={this.props.providerType}
- onToggleLedgerDialog={this.props.onToggleLedgerDialog}
- blockchain={this.props.blockchain}
- />
- );
- } else {
- // Nothing to connect to, show install/info popover
- return (
- <div className="px2" style={{ maxWidth: 420 }}>
- <div className="center h4 py2" style={{ color: colors.grey700 }}>
- Choose a wallet:
- </div>
- <div className="flex pb3">
- <div className="center px2">
- <div style={{ color: colors.darkGrey }}>Install a browser wallet</div>
- <div className="py2">
- <img src="/images/metamask_or_parity.png" width="135" />
- </div>
- <div>
- Use{' '}
- <a
- href={constants.URL_METAMASK_CHROME_STORE}
- target="_blank"
- style={{ color: colors.lightBlueA700 }}
- >
- Metamask
- </a>{' '}
- or{' '}
- <a
- href={constants.URL_PARITY_CHROME_STORE}
- target="_blank"
- style={{ color: colors.lightBlueA700 }}
- >
- Parity Signer
- </a>
- </div>
- </div>
- <div>
- <div
- className="pl1 ml1"
- style={{ borderLeft: `1px solid ${colors.grey300}`, height: 65 }}
- />
- <div className="py1">or</div>
- <div
- className="pl1 ml1"
- style={{ borderLeft: `1px solid ${colors.grey300}`, height: 68 }}
- />
- </div>
- <div className="px2 center">
- <div style={{ color: colors.darkGrey }}>Connect to a ledger hardware wallet</div>
- <div style={{ paddingTop: 21, paddingBottom: 29 }}>
- <img src="/images/ledger_icon.png" style={{ width: 80 }} />
- </div>
- <div>
- <RaisedButton
- style={{ width: '100%' }}
- label="Use Ledger"
- onClick={this.props.onToggleLedgerDialog}
- />
- </div>
- </div>
- </div>
- </div>
- );
+ private _renderPopoverContent(): React.ReactNode {
+ const accountState = this._getAccountState();
+ switch (accountState) {
+ case AccountState.Ready:
+ return (
+ <SimpleMenu>
+ <CopyAddressSimpleMenuItem userAddress={this.props.userAddress} />
+ <DifferentWalletSimpleMenuItem onClick={this.props.onToggleLedgerDialog} />
+ <GoToAccountManagementSimpleMenuItem />
+ </SimpleMenu>
+ );
+ case AccountState.Disconnected:
+ case AccountState.Locked:
+ case AccountState.Loading:
+ default:
+ return null;
}
}
private _renderIcon(): React.ReactNode {
@@ -154,7 +94,7 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
case AccountState.Loading:
return <CircularProgress size={ROOT_HEIGHT} thickness={2} />;
case AccountState.Locked:
- return <Lock color={colors.black} />;
+ return <Image src="/images/lock_icon.svg" height="20px" width="20px" />;
case AccountState.Disconnected:
return <ActionAccountBalanceWallet color={colors.mediumBlue} />;
default:
diff --git a/packages/website/ts/components/top_bar/provider_picker.tsx b/packages/website/ts/components/top_bar/provider_picker.tsx
deleted file mode 100644
index 7937f2e9d..000000000
--- a/packages/website/ts/components/top_bar/provider_picker.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import { colors, constants as sharedConstants } from '@0xproject/react-shared';
-import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton';
-import * as React from 'react';
-import { Blockchain } from 'ts/blockchain';
-import { Dispatcher } from 'ts/redux/dispatcher';
-import { ProviderType } from 'ts/types';
-
-interface ProviderPickerProps {
- networkId: number;
- injectedProviderName: string;
- providerType: ProviderType;
- onToggleLedgerDialog: () => void;
- dispatcher: Dispatcher;
- blockchain: Blockchain;
-}
-
-interface ProviderPickerState {}
-
-export class ProviderPicker extends React.Component<ProviderPickerProps, ProviderPickerState> {
- public render(): React.ReactNode {
- const isLedgerSelected = this.props.providerType === ProviderType.Ledger;
- const menuStyle = {
- padding: 10,
- paddingTop: 15,
- paddingBottom: 15,
- };
- // Show dropdown with two options
- return (
- <div style={{ width: 225, overflow: 'hidden' }}>
- <RadioButtonGroup name="provider" defaultSelected={this.props.providerType}>
- <RadioButton
- onClick={this._onProviderRadioChanged.bind(this, ProviderType.Injected)}
- style={{ ...menuStyle, backgroundColor: !isLedgerSelected && colors.grey50 }}
- value={ProviderType.Injected}
- label={this._renderLabel(this.props.injectedProviderName, !isLedgerSelected)}
- />
- <RadioButton
- onClick={this._onProviderRadioChanged.bind(this, ProviderType.Ledger)}
- style={{ ...menuStyle, backgroundColor: isLedgerSelected && colors.grey50 }}
- value={ProviderType.Ledger}
- label={this._renderLabel('Ledger Nano S', isLedgerSelected)}
- />
- </RadioButtonGroup>
- </div>
- );
- }
- private _renderLabel(title: string, shouldShowNetwork: boolean): React.ReactNode {
- const label = (
- <div className="flex">
- <div style={{ fontSize: 14 }}>{title}</div>
- {shouldShowNetwork && this._renderNetwork()}
- </div>
- );
- return label;
- }
- private _renderNetwork(): React.ReactNode {
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
- return (
- <div className="flex" style={{ marginTop: 1 }}>
- <div className="relative" style={{ width: 14, paddingLeft: 14 }}>
- <img
- src={`/images/network_icons/${networkName.toLowerCase()}.png`}
- className="absolute"
- style={{ top: 6, width: 10 }}
- />
- </div>
- <div style={{ color: colors.lightGrey, fontSize: 11 }}>{networkName}</div>
- </div>
- );
- }
- private _onProviderRadioChanged(value: string): void {
- if (value === ProviderType.Ledger) {
- this.props.onToggleLedgerDialog();
- } else {
- // tslint:disable-next-line:no-floating-promises
- this.props.blockchain.updateProviderToInjectedAsync();
- }
- }
-}
diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx
index fac6c131f..778536663 100644
--- a/packages/website/ts/components/top_bar/top_bar.tsx
+++ b/packages/website/ts/components/top_bar/top_bar.tsx
@@ -199,7 +199,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
cursor: 'pointer',
paddingTop: 16,
};
- const hoverActiveNode = (
+ const activeNode = (
<div className="flex relative" style={{ color: menuIconStyle.color }}>
<div style={{ paddingRight: 10 }}>{this.props.translate.get(Key.Developers, Deco.Cap)}</div>
<div className="absolute" style={{ paddingLeft: 3, right: 3, top: -2 }}>
@@ -224,7 +224,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
<div className={menuClasses}>
<div className="flex justify-between">
<DropDown
- hoverActiveNode={hoverActiveNode}
+ activeNode={activeNode}
popoverContent={popoverContent}
anchorOrigin={{ horizontal: 'middle', vertical: 'bottom' }}
targetOrigin={{ horizontal: 'middle', vertical: 'top' }}
diff --git a/packages/website/ts/components/ui/container.tsx b/packages/website/ts/components/ui/container.tsx
index fb718d731..edbf8814b 100644
--- a/packages/website/ts/components/ui/container.tsx
+++ b/packages/website/ts/components/ui/container.tsx
@@ -17,6 +17,7 @@ export interface ContainerProps {
maxHeight?: StringOrNum;
width?: StringOrNum;
height?: StringOrNum;
+ minWidth?: StringOrNum;
minHeight?: StringOrNum;
isHidden?: boolean;
className?: string;
diff --git a/packages/website/ts/components/ui/drop_down.tsx b/packages/website/ts/components/ui/drop_down.tsx
index 22cb942f8..4d5caef08 100644
--- a/packages/website/ts/components/ui/drop_down.tsx
+++ b/packages/website/ts/components/ui/drop_down.tsx
@@ -1,4 +1,4 @@
-import Popover, { PopoverAnimationVertical } from 'material-ui/Popover';
+import Popover from 'material-ui/Popover';
import * as React from 'react';
import { MaterialUIPosition } from 'ts/types';
@@ -7,13 +7,20 @@ const DEFAULT_STYLE = {
fontSize: 14,
};
-interface DropDownProps {
- hoverActiveNode: React.ReactNode;
+export enum DropdownMouseEvent {
+ Hover = 'hover',
+ Click = 'click',
+}
+
+export interface DropDownProps {
+ activeNode: React.ReactNode;
popoverContent: React.ReactNode;
anchorOrigin: MaterialUIPosition;
targetOrigin: MaterialUIPosition;
style?: React.CSSProperties;
zDepth?: number;
+ activateEvent?: DropdownMouseEvent;
+ closeEvent?: DropdownMouseEvent;
}
interface DropDownState {
@@ -25,6 +32,8 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
public static defaultProps: Partial<DropDownProps> = {
style: DEFAULT_STYLE,
zDepth: 1,
+ activateEvent: DropdownMouseEvent.Hover,
+ closeEvent: DropdownMouseEvent.Hover,
};
private _isHovering: boolean;
private _popoverCloseCheckIntervalId: number;
@@ -58,46 +67,61 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
onMouseEnter={this._onHover.bind(this)}
onMouseLeave={this._onHoverOff.bind(this)}
>
- {this.props.hoverActiveNode}
+ <div onClick={this._onActiveNodeClick.bind(this)}>{this.props.activeNode}</div>
<Popover
open={this.state.isDropDownOpen}
anchorEl={this.state.anchorEl}
anchorOrigin={this.props.anchorOrigin}
targetOrigin={this.props.targetOrigin}
onRequestClose={this._closePopover.bind(this)}
- useLayerForClickAway={false}
- animation={PopoverAnimationVertical}
+ useLayerForClickAway={this.props.closeEvent === DropdownMouseEvent.Click}
+ animated={false}
zDepth={this.props.zDepth}
>
- <div onMouseEnter={this._onHover.bind(this)} onMouseLeave={this._onHoverOff.bind(this)}>
+ <div
+ onMouseEnter={this._onHover.bind(this)}
+ onMouseLeave={this._onHoverOff.bind(this)}
+ onClick={this._closePopover.bind(this)}
+ >
{this.props.popoverContent}
</div>
</Popover>
</div>
);
}
+ private _onActiveNodeClick(event: React.FormEvent<HTMLInputElement>): void {
+ if (this.props.activateEvent === DropdownMouseEvent.Click) {
+ this.setState({
+ isDropDownOpen: true,
+ anchorEl: event.currentTarget,
+ });
+ }
+ }
private _onHover(event: React.FormEvent<HTMLInputElement>): void {
this._isHovering = true;
- this._checkIfShouldOpenPopover(event);
+ if (this.props.activateEvent === DropdownMouseEvent.Hover) {
+ this._checkIfShouldOpenPopover(event);
+ }
+ }
+ private _onHoverOff(): void {
+ this._isHovering = false;
}
private _checkIfShouldOpenPopover(event: React.FormEvent<HTMLInputElement>): void {
if (this.state.isDropDownOpen) {
return; // noop
}
-
this.setState({
isDropDownOpen: true,
anchorEl: event.currentTarget,
});
}
- private _onHoverOff(): void {
- this._isHovering = false;
- }
private _checkIfShouldClosePopover(): void {
- if (!this.state.isDropDownOpen || this._isHovering) {
+ if (!this.state.isDropDownOpen) {
return; // noop
}
- this._closePopover();
+ if (this.props.closeEvent === DropdownMouseEvent.Hover && !this._isHovering) {
+ this._closePopover();
+ }
}
private _closePopover(): void {
this.setState({
diff --git a/packages/website/ts/components/ui/simple_menu.tsx b/packages/website/ts/components/ui/simple_menu.tsx
new file mode 100644
index 000000000..74b8ef6ae
--- /dev/null
+++ b/packages/website/ts/components/ui/simple_menu.tsx
@@ -0,0 +1,88 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import * as CopyToClipboard from 'react-copy-to-clipboard';
+import { Link } from 'react-router-dom';
+
+import { Container } from 'ts/components/ui/container';
+import { Text } from 'ts/components/ui/text';
+import { colors } from 'ts/style/colors';
+import { WebsitePaths } from 'ts/types';
+
+export interface SimpleMenuProps {
+ minWidth?: number | string;
+}
+
+export const SimpleMenu: React.StatelessComponent<SimpleMenuProps> = ({ children, minWidth }) => {
+ return (
+ <Container
+ marginLeft="16px"
+ marginRight="16px"
+ marginBottom="16px"
+ minWidth={minWidth}
+ className="flex flex-column"
+ >
+ {children}
+ </Container>
+ );
+};
+
+SimpleMenu.defaultProps = {
+ minWidth: '220px',
+};
+
+export interface SimpleMenuItemProps {
+ displayText: string;
+ onClick?: () => void;
+}
+export const SimpleMenuItem: React.StatelessComponent<SimpleMenuItemProps> = ({ displayText, onClick }) => {
+ // Falling back to _.noop for onclick retains the hovering effect
+ return (
+ <Container marginTop="16px" className="flex flex-column">
+ <Text
+ fontSize="14px"
+ fontColor={colors.darkGrey}
+ onClick={onClick || _.noop}
+ hoverColor={colors.mediumBlue}
+ >
+ {displayText}
+ </Text>
+ </Container>
+ );
+};
+
+export interface CopyAddressSimpleMenuItemProps {
+ userAddress: string;
+ onClick?: () => void;
+}
+export const CopyAddressSimpleMenuItem: React.StatelessComponent<CopyAddressSimpleMenuItemProps> = ({
+ userAddress,
+ onClick,
+}) => {
+ return (
+ <CopyToClipboard text={userAddress}>
+ <SimpleMenuItem displayText="Copy Address to Clipboard" onClick={onClick} />
+ </CopyToClipboard>
+ );
+};
+
+export interface GoToAccountManagementSimpleMenuItemProps {
+ onClick?: () => void;
+}
+export const GoToAccountManagementSimpleMenuItem: React.StatelessComponent<
+ GoToAccountManagementSimpleMenuItemProps
+> = ({ onClick }) => {
+ return (
+ <Link to={`${WebsitePaths.Portal}/account`} style={{ textDecoration: 'none' }}>
+ <SimpleMenuItem displayText="Manage Account..." onClick={onClick} />
+ </Link>
+ );
+};
+
+export interface DifferentWalletSimpleMenuItemProps {
+ onClick?: () => void;
+}
+export const DifferentWalletSimpleMenuItem: React.StatelessComponent<DifferentWalletSimpleMenuItemProps> = ({
+ onClick,
+}) => {
+ return <SimpleMenuItem displayText="Use a Different Wallet..." onClick={onClick} />;
+};
diff --git a/packages/website/ts/components/ui/text.tsx b/packages/website/ts/components/ui/text.tsx
index c1cb2ade4..315f72854 100644
--- a/packages/website/ts/components/ui/text.tsx
+++ b/packages/website/ts/components/ui/text.tsx
@@ -3,7 +3,7 @@ import { darken } from 'polished';
import * as React from 'react';
import { styled } from 'ts/style/theme';
-export type TextTag = 'p' | 'div' | 'span' | 'label' | 'h1' | 'h2' | 'h3' | 'h4';
+export type TextTag = 'p' | 'div' | 'span' | 'label' | 'h1' | 'h2' | 'h3' | 'h4' | 'i';
export interface TextProps {
className?: string;
@@ -17,6 +17,7 @@ export interface TextProps {
fontWeight?: number | string;
textDecorationLine?: string;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
+ hoverColor?: string;
}
const PlainText: React.StatelessComponent<TextProps> = ({ children, className, onClick, Tag }) => (
@@ -37,7 +38,7 @@ export const Text = styled(PlainText)`
${props => (props.onClick ? 'cursor: pointer' : '')};
transition: color 0.5s ease;
&:hover {
- ${props => (props.onClick ? `color: ${darken(0.3, props.fontColor)}` : '')};
+ ${props => (props.onClick ? `color: ${props.hoverColor || darken(0.3, props.fontColor)}` : '')};
}
`;
diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx
index 1f1e3598a..1e0b9ec48 100644
--- a/packages/website/ts/components/wallet/wallet.tsx
+++ b/packages/website/ts/components/wallet/wallet.tsx
@@ -1,19 +1,25 @@
import { constants as sharedConstants, EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared';
import { BigNumber, errorUtils } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
import * as React from 'react';
-import { Link } from 'react-router-dom';
import firstBy = require('thenby');
import { Blockchain } from 'ts/blockchain';
import { AccountConnection } from 'ts/components/ui/account_connection';
import { Container } from 'ts/components/ui/container';
+import { DropDown, DropdownMouseEvent } from 'ts/components/ui/drop_down';
import { IconButton } from 'ts/components/ui/icon_button';
import { Identicon } from 'ts/components/ui/identicon';
import { Island } from 'ts/components/ui/island';
+import {
+ CopyAddressSimpleMenuItem,
+ DifferentWalletSimpleMenuItem,
+ GoToAccountManagementSimpleMenuItem,
+ SimpleMenu,
+ SimpleMenuItem,
+} from 'ts/components/ui/simple_menu';
import { Text } from 'ts/components/ui/text';
import { TokenIcon } from 'ts/components/ui/token_icon';
import { BodyOverlay } from 'ts/components/wallet/body_overlay';
@@ -34,7 +40,6 @@ import {
TokenByAddress,
TokenState,
TokenStateByAddress,
- WebsitePaths,
} from 'ts/types';
import { analytics } from 'ts/utils/analytics';
import { constants } from 'ts/utils/constants';
@@ -83,9 +88,7 @@ const ICON_DIMENSION = 28;
const BODY_ITEM_KEY = 'BODY';
const HEADER_ITEM_KEY = 'HEADER';
const ETHER_ITEM_KEY = 'ETHER';
-const USD_DECIMAL_PLACES = 2;
const NO_ALLOWANCE_TOGGLE_SPACE_WIDTH = 56;
-const ACCOUNT_PATH = `${WebsitePaths.Portal}/account`;
const PLACEHOLDER_COLOR = colors.grey300;
const LOADING_ROWS_COUNT = 6;
@@ -189,6 +192,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
);
}
private _renderConnectedHeaderRows(): React.ReactElement<{}> {
+ const isMobile = this.props.screenWidth === ScreenWidths.Sm;
const userAddress = this.props.userAddress;
const accountState = this._getAccountState();
const main = (
@@ -199,15 +203,49 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
<AccountConnection accountState={accountState} injectedProviderName={this.props.injectedProviderName} />
</div>
);
+ const onClick = _.noop;
+ const accessory = (
+ <DropDown
+ activeNode={
+ // this container gives the menu button more of a hover target for the drop down
+ // it prevents accidentally closing the menu by moving off of the button
+ <Container paddingLeft="100px" paddingRight="15px">
+ <Text
+ className="zmdi zmdi-more-horiz"
+ Tag="i"
+ fontSize="32px"
+ fontFamily="Material-Design-Iconic-Font"
+ fontColor={colors.darkGrey}
+ onClick={onClick}
+ hoverColor={colors.mediumBlue}
+ />
+ </Container>
+ }
+ popoverContent={
+ <SimpleMenu minWidth="150px">
+ <CopyAddressSimpleMenuItem userAddress={this.props.userAddress} />
+ <DifferentWalletSimpleMenuItem onClick={this.props.onToggleLedgerDialog} />
+ <SimpleMenuItem displayText="Add Tokens..." onClick={this.props.onAddToken} />
+ <SimpleMenuItem displayText="Remove Tokens..." onClick={this.props.onRemoveToken} />
+ <GoToAccountManagementSimpleMenuItem />
+ </SimpleMenu>
+ }
+ anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
+ targetOrigin={{ horizontal: 'right', vertical: 'top' }}
+ zDepth={1}
+ activateEvent={DropdownMouseEvent.Click}
+ closeEvent={isMobile ? DropdownMouseEvent.Click : DropdownMouseEvent.Hover}
+ />
+ );
return (
- <Link key={HEADER_ITEM_KEY} to={ACCOUNT_PATH} style={{ textDecoration: 'none' }}>
- <StandardIconRow
- icon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />}
- main={main}
- minHeight="60px"
- backgroundColor={colors.white}
- />
- </Link>
+ <StandardIconRow
+ key={HEADER_ITEM_KEY}
+ icon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />}
+ main={main}
+ accessory={accessory}
+ minHeight="60px"
+ backgroundColor={colors.white}
+ />
);
}
private _renderBody(): React.ReactElement<{}> {
@@ -320,8 +358,24 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection &&
!_.isUndefined(this.props.userEtherBalanceInWei);
const etherToken = this._getEthToken();
+ const wrapEtherItem = shouldShowWrapEtherItem ? (
+ <WrapEtherItem
+ userAddress={this.props.userAddress}
+ networkId={this.props.networkId}
+ blockchain={this.props.blockchain}
+ dispatcher={this.props.dispatcher}
+ userEtherBalanceInWei={this.props.userEtherBalanceInWei}
+ direction={accessoryItemConfig.wrappedEtherDirection}
+ etherToken={etherToken}
+ lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
+ onConversionSuccessful={this._closeWrappedEtherActionRow.bind(this)}
+ // tslint:disable:jsx-no-lambda
+ refetchEthTokenStateAsync={async () => this.props.refetchTokenStateAsync(etherToken.address)}
+ />
+ ) : null;
return (
<div id={key} key={key} className={`flex flex-column ${className || ''}`}>
+ {this.state.wrappedEtherDirection === Side.Receive && wrapEtherItem}
<StandardIconRow
icon={icon}
main={
@@ -331,23 +385,8 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
</div>
}
accessory={this._renderAccessoryItems(accessoryItemConfig)}
- backgroundColor={shouldShowWrapEtherItem ? colors.walletFocusedItemBackground : undefined}
/>
- {shouldShowWrapEtherItem && (
- <WrapEtherItem
- userAddress={this.props.userAddress}
- networkId={this.props.networkId}
- blockchain={this.props.blockchain}
- dispatcher={this.props.dispatcher}
- userEtherBalanceInWei={this.props.userEtherBalanceInWei}
- direction={accessoryItemConfig.wrappedEtherDirection}
- etherToken={etherToken}
- lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
- onConversionSuccessful={this._closeWrappedEtherActionRow.bind(this)}
- // tslint:disable:jsx-no-lambda
- refetchEthTokenStateAsync={async () => this.props.refetchTokenStateAsync(etherToken.address)}
- />
- )}
+ {this.state.wrappedEtherDirection === Side.Deposit && wrapEtherItem}
</div>
);
}
@@ -411,19 +450,11 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
price?: BigNumber,
isLoading: boolean = false,
): React.ReactNode {
- let result;
- if (!isLoading) {
- if (_.isUndefined(price)) {
- result = '--';
- } else {
- const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals);
- const value = unitAmount.mul(price);
- const formattedAmount = value.toFixed(USD_DECIMAL_PLACES);
- result = `$${formattedAmount}`;
- }
- } else {
- result = '$0.00';
- }
+ const result = !isLoading
+ ? _.isUndefined(price)
+ ? '--'
+ : utils.getUsdValueFormattedAmount(amount, decimals, price)
+ : '$0.00';
return (
<PlaceHolder hideChildren={isLoading} fillColor={PLACEHOLDER_COLOR}>
<Text fontSize="14px" fontColor={colors.darkGrey} lineHeight="1em">
diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx
index 851b35f90..2b4cf93fe 100644
--- a/packages/website/ts/components/wallet/wrap_ether_item.tsx
+++ b/packages/website/ts/components/wallet/wrap_ether_item.tsx
@@ -8,6 +8,7 @@ import * as React from 'react';
import { Blockchain } from 'ts/blockchain';
import { EthAmountInput } from 'ts/components/inputs/eth_amount_input';
import { TokenAmountInput } from 'ts/components/inputs/token_amount_input';
+import { Container } from 'ts/components/ui/container';
import { Dispatcher } from 'ts/redux/dispatcher';
import { colors } from 'ts/style/colors';
import { BlockchainCallErrs, Side, Token } from 'ts/types';
@@ -94,7 +95,7 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
const topLabelText = isWrappingEth ? 'Convert ETH into WETH 1:1' : 'Convert WETH into ETH 1:1';
return (
- <div className="flex" style={{ backgroundColor: colors.walletFocusedItemBackground }}>
+ <Container className="flex" backgroundColor={colors.walletFocusedItemBackground} paddingTop="25px">
<div>{this._renderIsEthConversionHappeningSpinner()} </div>
<div className="flex flex-column">
<div style={styles.topLabel}>{topLabelText}</div>
@@ -142,7 +143,7 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
{this._renderErrorMsg()}
</div>
- </div>
+ </Container>
);
}
private _onValueChange(_isValid: boolean, amount?: BigNumber): void {
diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts
index e8fdbc255..14e777d1a 100644
--- a/packages/website/ts/types.ts
+++ b/packages/website/ts/types.ts
@@ -488,6 +488,8 @@ export enum Providers {
Parity = 'PARITY',
Metamask = 'METAMASK',
Mist = 'MIST',
+ Toshi = 'TOSHI',
+ Cipher = 'CIPHER',
}
export interface InjectedProviderUpdate {
diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts
index d3a2aa107..0c4b88780 100644
--- a/packages/website/ts/utils/constants.ts
+++ b/packages/website/ts/utils/constants.ts
@@ -6,7 +6,7 @@ export const constants = {
ETHER_TOKEN_SYMBOL: 'WETH',
ZRX_TOKEN_SYMBOL: 'ZRX',
ETHER_SYMBOL: 'ETH',
- TOKEN_AMOUNT_DISPLAY_PRECISION: 5,
+ TOKEN_AMOUNT_DISPLAY_PRECISION: 4,
GENESIS_ORDER_BLOCK_BY_NETWORK_ID: {
1: 4145578,
42: 3117574,
@@ -29,6 +29,8 @@ export const constants = {
PROVIDER_NAME_METAMASK: 'MetaMask',
PROVIDER_NAME_PARITY_SIGNER: 'Parity Signer',
PROVIDER_NAME_MIST: 'Mist',
+ PROVIDER_NAME_CIPHER: 'Cipher Browser',
+ PROVIDER_NAME_TOSHI: 'Toshi',
PROVIDER_NAME_GENERIC: 'Injected Web3',
PROVIDER_NAME_PUBLIC: '0x Public',
ROLLBAR_ACCESS_TOKEN: 'a6619002b51c4464928201e6ea94de65',
@@ -38,6 +40,7 @@ export const constants = {
UNAVAILABLE_STATUS: 503,
TAKER_FEE: new BigNumber(0),
TESTNET_NAME: 'Kovan',
+ NUMERAL_USD_FORMAT: '$0,0.00',
PROJECT_URL_ETHFINEX: 'https://www.ethfinex.com/',
PROJECT_URL_AMADEUS: 'http://amadeusrelay.org',
PROJECT_URL_DDEX: 'https://ddex.io',
diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts
index 73dacc1d6..2cda41545 100644
--- a/packages/website/ts/utils/utils.ts
+++ b/packages/website/ts/utils/utils.ts
@@ -8,6 +8,8 @@ import * as bowser from 'bowser';
import deepEqual = require('deep-equal');
import * as _ from 'lodash';
import * as moment from 'moment';
+import * as numeral from 'numeral';
+
import {
AccountState,
BlockchainCallErrs,
@@ -321,6 +323,7 @@ export const utils = {
getProviderType(provider: Provider): Providers | string {
const constructorName = provider.constructor.name;
let parsedProviderName = constructorName;
+ // https://ethereum.stackexchange.com/questions/24266/elegant-way-to-detect-current-provider-int-web3-js
switch (constructorName) {
case 'EthereumProvider':
parsedProviderName = Providers.Mist;
@@ -334,6 +337,10 @@ export const utils = {
parsedProviderName = Providers.Parity;
} else if ((provider as any).isMetaMask) {
parsedProviderName = Providers.Metamask;
+ } else if (!_.isUndefined(_.get(window, 'SOFA'))) {
+ parsedProviderName = Providers.Toshi;
+ } else if (!_.isUndefined(_.get(window, '__CIPHER__'))) {
+ parsedProviderName = Providers.Cipher;
}
return parsedProviderName;
},
@@ -377,10 +384,20 @@ export const utils = {
},
getFormattedAmount(amount: BigNumber, decimals: number, symbol: string): string {
const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals);
- const precision = Math.min(constants.TOKEN_AMOUNT_DISPLAY_PRECISION, unitAmount.decimalPlaces());
- const formattedAmount = unitAmount.toFixed(precision);
+ // if the unit amount is less than 1, show the natural number of decimal places with a max of 4
+ // if the unit amount is greater than or equal to 1, show only 2 decimal places
+ const precision = unitAmount.lt(1)
+ ? Math.min(constants.TOKEN_AMOUNT_DISPLAY_PRECISION, unitAmount.decimalPlaces())
+ : 2;
+ const format = `0,0.${_.repeat('0', precision)}`;
+ const formattedAmount = numeral(unitAmount).format(format);
return `${formattedAmount} ${symbol}`;
},
+ getUsdValueFormattedAmount(amount: BigNumber, decimals: number, price: BigNumber): string {
+ const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals);
+ const value = unitAmount.mul(price);
+ return numeral(value).format(constants.NUMERAL_USD_FORMAT);
+ },
openUrl(url: string): void {
window.open(url, '_blank');
},