Implement ETH/WETH conversion and allowance toggle styling

This commit is contained in:
Brandon Millman 2018-03-20 20:55:11 -07:00
parent bed7d87b7f
commit dc3be992a3
12 changed files with 413 additions and 74 deletions

View File

@ -4,3 +4,4 @@
* Added new colors (#468)
* Fix section and menuItem text display to replace dashes with spaces.
* Reorganized colors and added new ones

View File

@ -1,7 +1,6 @@
import { colors as materialUiColors } from 'material-ui/styles';
export const colors = {
...materialUiColors,
const baseColors = {
gray40: '#F8F8F8',
grey50: '#FAFAFA',
grey100: '#F5F5F5',
@ -27,6 +26,7 @@ export const colors = {
lightBlue: '#60A4F4',
lightBlueA700: '#0091EA',
linkBlue: '#1D5CDE',
mediumBlue: '#488AEA',
darkBlue: '#4D5481',
turquois: '#058789',
lightPurple: '#A81CA6',
@ -45,7 +45,22 @@ export const colors = {
orange: '#E69D00',
amber800: '#FF8F00',
darkYellow: '#caca03',
walletBoxShadow: 'rgba(56, 59, 137, 0.2)',
walletBorder: '#f5f5f6',
walletDefaultItemBackground: '#fbfbfc',
};
const appColors = {
// wallet specific colors
walletBoxShadow: 'rgba(56, 59, 137, 0.2)',
walletBorder: '#ededee',
walletDefaultItemBackground: '#fbfbfc',
walletFocusedItemBackground: '#f0f1f4',
allowanceToggleShadow: 'rgba(0, 0, 0, 0)',
allowanceToggleOffTrack: '#adadad',
allowanceToggleOnTrack: baseColors.mediumBlue,
wrapEtherConfirmationButton: baseColors.mediumBlue,
};
export const colors = {
...materialUiColors,
...baseColors,
...appColors,
};

View File

@ -104,6 +104,8 @@ export class EthWethConversionDialog extends React.Component<
onChange={this._onValueChange.bind(this)}
amount={this.state.value}
onVisitBalancesPageClick={this.props.onCancelled}
shouldShowErrs={true}
shouldShowUnderline={true}
/>
) : (
<EthAmountInput

View File

@ -1,4 +1,4 @@
import { constants as sharedConstants } from '@0xproject/react-shared';
import { colors, constants as sharedConstants, Styles } from '@0xproject/react-shared';
import { BigNumber, logUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import Toggle from 'material-ui/Toggle';
@ -30,6 +30,31 @@ interface AllowanceToggleState {
prevAllowance: BigNumber;
}
const styles: Styles = {
baseThumbStyle: {
height: 10,
width: 10,
top: 6,
backgroundColor: colors.white,
boxShadow: `0px 0px 0px ${colors.allowanceToggleShadow}`,
},
offThumbStyle: {
left: 4,
},
onThumbStyle: {
left: 25,
},
baseTrackStyle: {
width: 25,
},
offTrackStyle: {
backgroundColor: colors.allowanceToggleOffTrack,
},
onTrackStyle: {
backgroundColor: colors.allowanceToggleOnTrack,
},
};
export class AllowanceToggle extends React.Component<AllowanceToggleProps, AllowanceToggleState> {
constructor(props: AllowanceToggleProps) {
super(props);
@ -54,6 +79,10 @@ export class AllowanceToggle extends React.Component<AllowanceToggleProps, Allow
disabled={this.state.isSpinnerVisible || this.props.isDisabled}
toggled={this._isAllowanceSet()}
onToggle={this._onToggleAllowanceAsync.bind(this)}
thumbStyle={{ ...styles.baseThumbStyle, ...styles.offThumbStyle }}
thumbSwitchedStyle={{ ...styles.baseThumbStyle, ...styles.onThumbStyle }}
trackStyle={{ ...styles.baseTrackStyle, ...styles.offTrackStyle }}
trackSwitchedStyle={{ ...styles.baseTrackStyle, ...styles.onTrackStyle }}
/>
</div>
{this.state.isSpinnerVisible && (

View File

@ -12,6 +12,7 @@ interface BalanceBoundedInputProps {
label?: string;
balance: BigNumber;
amount?: BigNumber;
hintText?: string;
onChange: ValidatedBigNumberCallback;
shouldShowIncompleteErrs?: boolean;
shouldCheckBalance: boolean;
@ -19,6 +20,8 @@ interface BalanceBoundedInputProps {
onVisitBalancesPageClick?: () => void;
shouldHideVisitBalancesLink?: boolean;
isDisabled?: boolean;
shouldShowErrs?: boolean;
shouldShowUnderline?: boolean;
}
interface BalanceBoundedInputState {
@ -31,6 +34,9 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
shouldShowIncompleteErrs: false,
shouldHideVisitBalancesLink: false,
isDisabled: false,
shouldShowErrs: true,
hintText: 'amount',
shouldShowUnderline: true,
};
constructor(props: BalanceBoundedInputProps) {
super(props);
@ -71,9 +77,12 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
}
}
public render() {
let errorText = this.state.errMsg;
if (this.props.shouldShowIncompleteErrs && this.state.amountString === '') {
errorText = 'This field is required';
let errorText;
if (this.props.shouldShowErrs) {
errorText =
this.props.shouldShowIncompleteErrs && this.state.amountString === ''
? 'This field is required'
: this.state.errMsg;
}
let label: React.ReactNode | string = '';
if (!_.isUndefined(this.props.label)) {
@ -87,9 +96,10 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
floatingLabelStyle={{ color: colors.grey, width: 206 }}
errorText={errorText}
value={this.state.amountString}
hintText={<span style={{ textTransform: 'capitalize' }}>amount</span>}
hintText={<span style={{ textTransform: 'capitalize' }}>{this.props.hintText}</span>}
onChange={this._onValueChange.bind(this)}
underlineStyle={{ width: 'calc(100% + 50px)' }}
underlineShow={this.props.shouldShowUnderline}
disabled={this.props.isDisabled}
/>
);

View File

@ -10,22 +10,31 @@ interface EthAmountInputProps {
label?: string;
balance: BigNumber;
amount?: BigNumber;
hintText?: string;
onChange: ValidatedBigNumberCallback;
shouldShowIncompleteErrs: boolean;
onVisitBalancesPageClick?: () => void;
shouldCheckBalance: boolean;
shouldHideVisitBalancesLink?: boolean;
shouldShowErrs?: boolean;
shouldShowUnderline?: boolean;
style?: React.CSSProperties;
}
interface EthAmountInputState {}
export class EthAmountInput extends React.Component<EthAmountInputProps, EthAmountInputState> {
public static defaultProps: Partial<EthAmountInputProps> = {
shouldShowErrs: true,
shouldShowUnderline: true,
style: { height: 63 },
};
public render() {
const amount = this.props.amount
? ZeroEx.toUnitAmount(this.props.amount, constants.DECIMAL_PLACES_ETH)
: undefined;
return (
<div className="flex overflow-hidden" style={{ height: 63 }}>
<div className="flex overflow-hidden" style={this.props.style}>
<BalanceBoundedInput
label={this.props.label}
balance={this.props.balance}
@ -35,6 +44,9 @@ export class EthAmountInput extends React.Component<EthAmountInputProps, EthAmou
shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs}
onVisitBalancesPageClick={this.props.onVisitBalancesPageClick}
shouldHideVisitBalancesLink={this.props.shouldHideVisitBalancesLink}
hintText={this.props.hintText}
shouldShowErrs={this.props.shouldShowErrs}
shouldShowUnderline={this.props.shouldShowUnderline}
/>
<div style={{ paddingTop: _.isUndefined(this.props.label) ? 15 : 40 }}>ETH</div>
</div>

View File

@ -15,12 +15,16 @@ interface TokenAmountInputProps {
token: Token;
label?: string;
amount?: BigNumber;
hintText?: string;
shouldShowIncompleteErrs: boolean;
shouldCheckBalance: boolean;
shouldCheckAllowance: boolean;
onChange: ValidatedBigNumberCallback;
onVisitBalancesPageClick?: () => void;
lastForceTokenStateRefetch: number;
shouldShowErrs?: boolean;
shouldShowUnderline?: boolean;
style?: React.CSSProperties;
}
interface TokenAmountInputState {
@ -30,6 +34,10 @@ interface TokenAmountInputState {
}
export class TokenAmountInput extends React.Component<TokenAmountInputProps, TokenAmountInputState> {
public static defaultProps: Partial<TokenAmountInputProps> = {
shouldShowErrs: true,
shouldShowUnderline: true,
};
private _isUnmounted: boolean;
constructor(props: TokenAmountInputProps) {
super(props);
@ -64,8 +72,9 @@ export class TokenAmountInput extends React.Component<TokenAmountInputProps, Tok
? ZeroEx.toUnitAmount(this.props.amount, this.props.token.decimals)
: undefined;
const hasLabel = !_.isUndefined(this.props.label);
const style = !_.isUndefined(this.props.style) ? this.props.style : { height: hasLabel ? 84 : 62 };
return (
<div className="flex overflow-hidden" style={{ height: hasLabel ? 84 : 62 }}>
<div className="flex overflow-hidden" style={style}>
<BalanceBoundedInput
label={this.props.label}
amount={amount}
@ -76,6 +85,9 @@ export class TokenAmountInput extends React.Component<TokenAmountInputProps, Tok
shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs}
onVisitBalancesPageClick={this.props.onVisitBalancesPageClick}
isDisabled={!this.state.isBalanceAndAllowanceLoaded}
hintText={this.props.hintText}
shouldShowErrs={this.props.shouldShowErrs}
shouldShowUnderline={this.props.shouldShowUnderline}
/>
<div style={{ paddingTop: hasLabel ? 39 : 14 }}>{this.props.token.symbol}</div>
</div>

View File

@ -19,7 +19,7 @@ import { TokenBalances } from 'ts/components/token_balances';
import { TopBar } from 'ts/components/top_bar/top_bar';
import { TradeHistory } from 'ts/components/trade_history/trade_history';
import { FlashMessage } from 'ts/components/ui/flash_message';
import { Wallet } from 'ts/components/wallet';
import { Wallet } from 'ts/components/wallet/wallet';
import { GenerateOrderForm } from 'ts/containers/generate_order_form';
import { localStorage } from 'ts/local_storage/local_storage';
import { Dispatcher } from 'ts/redux/dispatcher';

View File

@ -12,6 +12,7 @@ import FlatButton from 'material-ui/FlatButton';
import { List, ListItem } from 'material-ui/List';
import NavigationArrowDownward from 'material-ui/svg-icons/navigation/arrow-downward';
import NavigationArrowUpward from 'material-ui/svg-icons/navigation/arrow-upward';
import Close from 'material-ui/svg-icons/navigation/close';
import * as React from 'react';
import ReactTooltip = require('react-tooltip');
import firstBy = require('thenby');
@ -20,14 +21,16 @@ import { Blockchain } from 'ts/blockchain';
import { AllowanceToggle } from 'ts/components/inputs/allowance_toggle';
import { Identicon } from 'ts/components/ui/identicon';
import { TokenIcon } from 'ts/components/ui/token_icon';
import { WrapEtherItem } from 'ts/components/wallet/wrap_ether_item';
import { Dispatcher } from 'ts/redux/dispatcher';
import { BalanceErrs, BlockchainErrs, Token, TokenByAddress, TokenState, TokenStateByAddress } from 'ts/types';
import { BalanceErrs, BlockchainErrs, Side, Token, TokenByAddress, TokenState, TokenStateByAddress } from 'ts/types';
import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles';
export interface WalletProps {
userAddress?: string;
networkId?: number;
userAddress: string;
networkId: number;
blockchain: Blockchain;
blockchainIsLoaded: boolean;
blockchainErr: BlockchainErrs;
@ -40,11 +43,7 @@ export interface WalletProps {
interface WalletState {
trackedTokenStateByAddress: TokenStateByAddress;
}
enum WrappedEtherAction {
Wrap,
Unwrap,
wrappedEtherDirection?: Side;
}
interface AllowanceToggleConfig {
@ -53,7 +52,7 @@ interface AllowanceToggleConfig {
}
interface AccessoryItemConfig {
wrappedEtherAction?: WrappedEtherAction;
wrappedEtherDirection?: Side;
allowanceToggleConfig?: AllowanceToggleConfig;
}
@ -87,20 +86,19 @@ const styles: Styles = {
},
tokenItem: {
backgroundColor: colors.walletDefaultItemBackground,
paddingTop: 8,
paddingBottom: 8,
},
headerItem: {
paddingTop: 8,
paddingBottom: 8,
},
wrappedEtherButtonLabel: {
fontSize: 12,
wrappedEtherOpenButtonLabel: {
fontSize: 10,
},
amountLabel: {
fontWeight: 'bold',
color: colors.black,
},
paddedItem: {
paddingTop: 8,
paddingBottom: 8,
},
accessoryItemsContainer: { width: 150, right: 8 },
};
const ETHER_ICON_PATH = '/images/ether.png';
@ -118,6 +116,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
const initialTrackedTokenStateByAddress = this._getInitialTrackedTokenStateByAddress(props.trackedTokens);
this.state = {
trackedTokenStateByAddress: initialTrackedTokenStateByAddress,
wrappedEtherDirection: undefined,
};
}
public componentWillMount() {
@ -182,16 +181,14 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
<ListItem
primaryText={primaryText}
leftIcon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />}
style={{ ...styles.headerItem, ...styles.borderedItem }}
style={{ ...styles.paddedItem, ...styles.borderedItem }}
innerDivStyle={styles.headerItemInnerDiv}
/>
);
}
private _renderFooterRows() {
const primaryText = '+ other tokens';
return (
<ListItem primaryText={primaryText} style={styles.borderedItem} innerDivStyle={styles.footerItemInnerDiv} />
);
return <ListItem primaryText={primaryText} innerDivStyle={styles.footerItemInnerDiv} />;
}
private _renderEthRows() {
const primaryText = this._renderAmount(
@ -200,16 +197,40 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
ETHER_SYMBOL,
);
const accessoryItemConfig = {
wrappedEtherAction: WrappedEtherAction.Wrap,
wrappedEtherDirection: Side.Deposit,
};
const isInWrappedEtherState =
!_.isUndefined(this.state.wrappedEtherDirection) &&
this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection;
const style = isInWrappedEtherState
? { ...walletItemStyles.focusedItem, ...styles.paddedItem }
: { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem };
const etherToken = this._getEthToken();
return (
<ListItem
primaryText={primaryText}
leftIcon={<img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />}
rightAvatar={this._renderAccessoryItems(accessoryItemConfig)}
style={{ ...styles.tokenItem, ...styles.borderedItem }}
innerDivStyle={styles.tokenItemInnerDiv}
/>
<div>
<ListItem
primaryText={primaryText}
leftIcon={<img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />}
rightAvatar={this._renderAccessoryItems(accessoryItemConfig)}
disableTouchRipple={true}
style={style}
innerDivStyle={styles.tokenItemInnerDiv}
/>
{isInWrappedEtherState && (
<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)}
refetchEthTokenStateAsync={this._refetchTokenStateAsync.bind(this, etherToken.address)}
/>
)}
</div>
);
}
private _renderTokenRows() {
@ -229,32 +250,56 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
EtherscanLinkSuffixes.Address,
);
const amount = this._renderAmount(tokenState.balance, token.decimals, token.symbol);
const wrappedEtherAction = token.symbol === ETHER_TOKEN_SYMBOL ? WrappedEtherAction.Unwrap : undefined;
const wrappedEtherDirection = token.symbol === ETHER_TOKEN_SYMBOL ? Side.Receive : undefined;
const accessoryItemConfig: AccessoryItemConfig = {
wrappedEtherAction,
wrappedEtherDirection,
allowanceToggleConfig: {
token,
tokenState,
},
};
const shouldShowWrapEtherItem =
!_.isUndefined(this.state.wrappedEtherDirection) &&
this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection;
const style = shouldShowWrapEtherItem
? { ...walletItemStyles.focusedItem, ...styles.paddedItem }
: { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem };
const etherToken = this._getEthToken();
return (
<ListItem
primaryText={amount}
leftIcon={this._renderTokenIcon(token, tokenLink)}
rightAvatar={this._renderAccessoryItems(accessoryItemConfig)}
style={{ ...styles.tokenItem, ...styles.borderedItem }}
innerDivStyle={styles.tokenItemInnerDiv}
/>
<div>
<ListItem
primaryText={amount}
leftIcon={this._renderTokenIcon(token, tokenLink)}
rightAvatar={this._renderAccessoryItems(accessoryItemConfig)}
disableTouchRipple={true}
style={style}
innerDivStyle={styles.tokenItemInnerDiv}
/>
{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)}
refetchEthTokenStateAsync={this._refetchTokenStateAsync.bind(this, etherToken.address)}
/>
)}
</div>
);
}
private _renderAccessoryItems(config: AccessoryItemConfig) {
const shouldShowWrappedEtherAction = !_.isUndefined(config.wrappedEtherAction);
const shouldShowWrappedEtherAction = !_.isUndefined(config.wrappedEtherDirection);
const shouldShowToggle = !_.isUndefined(config.allowanceToggleConfig);
return (
<div style={{ width: 160 }}>
<div style={styles.accessoryItemsContainer}>
<div className="flex">
<div className="flex-auto">
{shouldShowWrappedEtherAction && this._renderWrappedEtherButton(config.wrappedEtherAction)}
{shouldShowWrappedEtherAction && this._renderWrappedEtherButton(config.wrappedEtherDirection)}
</div>
<div className="flex-last py1">
{shouldShowToggle && this._renderAllowanceToggle(config.allowanceToggleConfig)}
@ -297,28 +342,38 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
);
}
}
private _renderWrappedEtherButton(action: WrappedEtherAction) {
private _renderWrappedEtherButton(wrappedEtherDirection: Side) {
const isWrappedEtherDirectionOpen = this.state.wrappedEtherDirection === wrappedEtherDirection;
let buttonLabel;
let buttonIcon;
switch (action) {
case WrappedEtherAction.Wrap:
buttonLabel = 'wrap';
buttonIcon = <NavigationArrowDownward />;
break;
case WrappedEtherAction.Unwrap:
buttonLabel = 'unwrap';
buttonIcon = <NavigationArrowUpward />;
break;
default:
throw utils.spawnSwitchErr('wrappedEtherAction', action);
if (isWrappedEtherDirectionOpen) {
buttonLabel = 'cancel';
buttonIcon = <Close />;
} else {
switch (wrappedEtherDirection) {
case Side.Deposit:
buttonLabel = 'wrap';
buttonIcon = <NavigationArrowDownward />;
break;
case Side.Receive:
buttonLabel = 'unwrap';
buttonIcon = <NavigationArrowUpward />;
break;
default:
throw utils.spawnSwitchErr('wrappedEtherDirection', wrappedEtherDirection);
}
}
const onClick = isWrappedEtherDirectionOpen
? this._closeWrappedEtherActionRow.bind(this)
: this._openWrappedEtherActionRow.bind(this, wrappedEtherDirection);
return (
<FlatButton
label={buttonLabel}
labelPosition="after"
primary={true}
icon={buttonIcon}
labelStyle={styles.wrappedEtherButtonLabel}
labelStyle={styles.wrappedEtherOpenButtonLabel}
onClick={onClick}
/>
);
}
@ -370,4 +425,19 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
},
});
}
private _openWrappedEtherActionRow(wrappedEtherDirection: Side) {
this.setState({
wrappedEtherDirection,
});
}
private _closeWrappedEtherActionRow() {
this.setState({
wrappedEtherDirection: undefined,
});
}
private _getEthToken() {
const tokens = _.values(this.props.tokenByAddress);
const etherToken = _.find(tokens, { symbol: ETHER_TOKEN_SYMBOL });
return etherToken;
}
}

View File

@ -0,0 +1,185 @@
import { ZeroEx } from '0x.js';
import { colors, Styles } from '@0xproject/react-shared';
import { BigNumber, logUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import FlatButton from 'material-ui/FlatButton';
import { ListItem } from 'material-ui/List';
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 { Dispatcher } from 'ts/redux/dispatcher';
import { BlockchainCallErrs, Side, Token } from 'ts/types';
import { constants } from 'ts/utils/constants';
import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils';
import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles';
export interface WrapEtherItemProps {
userAddress: string;
networkId: number;
blockchain: Blockchain;
dispatcher: Dispatcher;
userEtherBalanceInWei: BigNumber;
direction: Side;
etherToken: Token;
lastForceTokenStateRefetch: number;
onConversionSuccessful?: () => void;
refetchEthTokenStateAsync: () => Promise<void>;
}
interface WrapEtherItemState {
currentInputAmount?: BigNumber;
currentInputHasErrors: boolean;
isEthConversionHappening: boolean;
}
const styles: Styles = {
topLabel: { color: colors.black, fontSize: 11 },
inputContainer: {
backgroundColor: colors.white,
borderBottomRightRadius: 3,
borderBottomLeftRadius: 3,
borderTopRightRadius: 3,
borderTopLeftRadius: 3,
padding: 4,
width: 125,
},
ethAmountInput: { height: 32 },
innerDiv: { paddingLeft: 60, paddingTop: 0 },
wrapEtherConfirmationButtonContainer: { width: 128, top: 16 },
wrapEtherConfirmationButtonLabel: {
fontSize: 10,
color: colors.white,
},
};
export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEtherItemState> {
constructor(props: WrapEtherItemProps) {
super(props);
this.state = {
currentInputAmount: undefined,
currentInputHasErrors: false,
isEthConversionHappening: false,
};
}
public render() {
const etherBalanceInEth = ZeroEx.toUnitAmount(this.props.userEtherBalanceInWei, constants.DECIMAL_PLACES_ETH);
const isWrappingEth = this.props.direction === Side.Deposit;
const topLabelText = isWrappingEth ? 'Convert ETH into WETH 1:1' : 'Convert WETH into ETH 1:1';
return (
<ListItem
primaryText={
<div>
<div style={styles.topLabel}>{topLabelText}</div>
<div style={styles.inputContainer}>
{isWrappingEth ? (
<EthAmountInput
balance={etherBalanceInEth}
amount={this.state.currentInputAmount}
hintText={'0.00'}
onChange={this._onValueChange.bind(this)}
shouldCheckBalance={true}
shouldShowIncompleteErrs={false}
shouldShowErrs={false}
shouldShowUnderline={false}
style={styles.ethAmountInput}
/>
) : (
<TokenAmountInput
lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
blockchain={this.props.blockchain}
userAddress={this.props.userAddress}
networkId={this.props.networkId}
token={this.props.etherToken}
shouldShowIncompleteErrs={false}
shouldCheckBalance={true}
shouldCheckAllowance={false}
onChange={this._onValueChange.bind(this)}
amount={this.state.currentInputAmount}
hintText={'0.00'}
onVisitBalancesPageClick={_.noop}
shouldShowErrs={false}
shouldShowUnderline={false}
style={styles.ethAmountInput}
/>
)}
</div>
</div>
}
secondaryTextLines={2}
disableTouchRipple={true}
style={walletItemStyles.focusedItem}
innerDivStyle={styles.innerDiv}
leftIcon={this.state.isEthConversionHappening && this._renderIsEthConversionHappeningSpinner()}
rightAvatar={this._renderWrapEtherConfirmationButton()}
/>
);
}
private _onValueChange(isValid: boolean, amount?: BigNumber) {
this.setState({
currentInputAmount: amount,
currentInputHasErrors: !isValid,
});
}
private _renderIsEthConversionHappeningSpinner() {
return (
<div className="pl1" style={{ paddingTop: 10 }}>
<i className="zmdi zmdi-spinner zmdi-hc-spin" />
</div>
);
}
private _renderWrapEtherConfirmationButton() {
const isWrappingEth = this.props.direction === Side.Deposit;
const labelText = isWrappingEth ? 'wrap' : 'unwrap';
return (
<div style={styles.wrapEtherConfirmationButtonContainer}>
<FlatButton
backgroundColor={colors.wrapEtherConfirmationButton}
label={labelText}
labelStyle={styles.wrapEtherConfirmationButtonLabel}
onClick={this._wrapEtherConfirmationAction.bind(this)}
disabled={this.state.isEthConversionHappening}
/>
</div>
);
}
private async _wrapEtherConfirmationAction() {
this.setState({
isEthConversionHappening: true,
});
try {
const etherToken = this.props.etherToken;
const amountToConvert = this.state.currentInputAmount;
if (this.props.direction === Side.Deposit) {
await this.props.blockchain.convertEthToWrappedEthTokensAsync(etherToken.address, amountToConvert);
const ethAmount = ZeroEx.toUnitAmount(amountToConvert, constants.DECIMAL_PLACES_ETH);
this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount.toString()} ETH to WETH`);
} else {
await this.props.blockchain.convertWrappedEthTokensToEthAsync(etherToken.address, amountToConvert);
const tokenAmount = ZeroEx.toUnitAmount(amountToConvert, etherToken.decimals);
this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount.toString()} WETH to ETH`);
}
await this.props.refetchEthTokenStateAsync();
this.props.onConversionSuccessful();
} catch (err) {
const errMsg = `${err}`;
if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) {
this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
} else if (!utils.didUserDenyWeb3Request(errMsg)) {
logUtils.log(`Unexpected error encountered: ${err}`);
logUtils.log(err.stack);
const errorMsg =
this.props.direction === Side.Deposit
? 'Failed to wrap your ETH. Please try again.'
: 'Failed to unwrap your WETH. Please try again.';
this.props.dispatcher.showFlashMessage(errorMsg);
await errorReporter.reportAsync(err);
}
}
this.setState({
isEthConversionHappening: false,
});
}
}

View File

@ -9,9 +9,9 @@ export const muiTheme = getMuiTheme({
},
palette: {
accent1Color: colors.lightBlueA700,
pickerHeaderColor: colors.lightBlue,
primary1Color: colors.lightBlue,
primary2Color: colors.lightBlue,
pickerHeaderColor: colors.mediumBlue,
primary1Color: colors.mediumBlue,
primary2Color: colors.mediumBlue,
textColor: colors.grey700,
},
datePicker: {
@ -29,8 +29,4 @@ export const muiTheme = getMuiTheme({
selectColor: colors.darkestGrey,
selectTextColor: colors.darkestGrey,
},
toggle: {
thumbOnColor: colors.limeGreen,
trackOnColor: colors.lightGreen,
},
});

View File

@ -0,0 +1,7 @@
import { colors, Styles } from '@0xproject/react-shared';
export const styles: Styles = {
focusedItem: {
backgroundColor: colors.walletFocusedItemBackground,
},
};