From dc3be992a3a1d5f352b65effa5c05f69f8e3272c Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 20 Mar 2018 20:55:11 -0700 Subject: Implement ETH/WETH conversion and allowance toggle styling --- packages/react-shared/CHANGELOG.md | 1 + packages/react-shared/src/utils/colors.ts | 21 +- .../dialogs/eth_weth_conversion_dialog.tsx | 2 + .../ts/components/inputs/allowance_toggle.tsx | 31 +- .../ts/components/inputs/balance_bounded_input.tsx | 18 +- .../ts/components/inputs/eth_amount_input.tsx | 14 +- .../ts/components/inputs/token_amount_input.tsx | 14 +- packages/website/ts/components/portal.tsx | 2 +- packages/website/ts/components/wallet.tsx | 373 ----------------- packages/website/ts/components/wallet/wallet.tsx | 443 +++++++++++++++++++++ .../ts/components/wallet/wrap_ether_item.tsx | 185 +++++++++ packages/website/ts/utils/mui_theme.ts | 10 +- packages/website/ts/utils/wallet_item_styles.ts | 7 + 13 files changed, 730 insertions(+), 391 deletions(-) delete mode 100644 packages/website/ts/components/wallet.tsx create mode 100644 packages/website/ts/components/wallet/wallet.tsx create mode 100644 packages/website/ts/components/wallet/wrap_ether_item.tsx create mode 100644 packages/website/ts/utils/wallet_item_styles.ts (limited to 'packages') diff --git a/packages/react-shared/CHANGELOG.md b/packages/react-shared/CHANGELOG.md index 51fb8e4b6..9fffd8ea3 100644 --- a/packages/react-shared/CHANGELOG.md +++ b/packages/react-shared/CHANGELOG.md @@ -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 diff --git a/packages/react-shared/src/utils/colors.ts b/packages/react-shared/src/utils/colors.ts index ea0165305..7613414ae 100644 --- a/packages/react-shared/src/utils/colors.ts +++ b/packages/react-shared/src/utils/colors.ts @@ -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', +}; + +const appColors = { + // wallet specific colors walletBoxShadow: 'rgba(56, 59, 137, 0.2)', - walletBorder: '#f5f5f6', + 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, }; 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 d1bdb447f..f61b6495e 100644 --- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx +++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx @@ -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} /> ) : ( { constructor(props: AllowanceToggleProps) { super(props); @@ -54,6 +79,10 @@ export class AllowanceToggle extends React.Component {this.state.isSpinnerVisible && ( diff --git a/packages/website/ts/components/inputs/balance_bounded_input.tsx b/packages/website/ts/components/inputs/balance_bounded_input.tsx index 253b01871..e9b8dd369 100644 --- a/packages/website/ts/components/inputs/balance_bounded_input.tsx +++ b/packages/website/ts/components/inputs/balance_bounded_input.tsx @@ -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.Componentamount} + hintText={{this.props.hintText}} onChange={this._onValueChange.bind(this)} underlineStyle={{ width: 'calc(100% + 50px)' }} + underlineShow={this.props.shouldShowUnderline} disabled={this.props.isDisabled} /> ); diff --git a/packages/website/ts/components/inputs/eth_amount_input.tsx b/packages/website/ts/components/inputs/eth_amount_input.tsx index a66f92c8c..f3a879065 100644 --- a/packages/website/ts/components/inputs/eth_amount_input.tsx +++ b/packages/website/ts/components/inputs/eth_amount_input.tsx @@ -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 { + public static defaultProps: Partial = { + 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 ( -
+
ETH
diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx index b55840fc4..591be7c0c 100644 --- a/packages/website/ts/components/inputs/token_amount_input.tsx +++ b/packages/website/ts/components/inputs/token_amount_input.tsx @@ -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 { + public static defaultProps: Partial = { + shouldShowErrs: true, + shouldShowUnderline: true, + }; private _isUnmounted: boolean; constructor(props: TokenAmountInputProps) { super(props); @@ -64,8 +72,9 @@ export class TokenAmountInput extends React.Component +
{this.props.token.symbol}
diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx index 59eaca67e..55013e105 100644 --- a/packages/website/ts/components/portal.tsx +++ b/packages/website/ts/components/portal.tsx @@ -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'; diff --git a/packages/website/ts/components/wallet.tsx b/packages/website/ts/components/wallet.tsx deleted file mode 100644 index 8c6ef9cad..000000000 --- a/packages/website/ts/components/wallet.tsx +++ /dev/null @@ -1,373 +0,0 @@ -import { ZeroEx } from '0x.js'; -import { - colors, - constants as sharedConstants, - EtherscanLinkSuffixes, - Styles, - utils as sharedUtils, -} from '@0xproject/react-shared'; -import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; -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 * as React from 'react'; -import ReactTooltip = require('react-tooltip'); -import firstBy = require('thenby'); - -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 { Dispatcher } from 'ts/redux/dispatcher'; -import { BalanceErrs, BlockchainErrs, Token, TokenByAddress, TokenState, TokenStateByAddress } from 'ts/types'; -import { constants } from 'ts/utils/constants'; -import { utils } from 'ts/utils/utils'; - -export interface WalletProps { - userAddress?: string; - networkId?: number; - blockchain: Blockchain; - blockchainIsLoaded: boolean; - blockchainErr: BlockchainErrs; - dispatcher: Dispatcher; - tokenByAddress: TokenByAddress; - trackedTokens: Token[]; - userEtherBalanceInWei: BigNumber; - lastForceTokenStateRefetch: number; -} - -interface WalletState { - trackedTokenStateByAddress: TokenStateByAddress; -} - -enum WrappedEtherAction { - Wrap, - Unwrap, -} - -interface AllowanceToggleConfig { - token: Token; - tokenState: TokenState; -} - -interface AccessoryItemConfig { - wrappedEtherAction?: WrappedEtherAction; - allowanceToggleConfig?: AllowanceToggleConfig; -} - -const styles: Styles = { - wallet: { - width: 346, - backgroundColor: colors.white, - borderBottomRightRadius: 10, - borderBottomLeftRadius: 10, - borderTopRightRadius: 10, - borderTopLeftRadius: 10, - boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, - overflow: 'hidden', - }, - list: { - padding: 0, - }, - tokenItemInnerDiv: { - paddingLeft: 60, - }, - headerItemInnerDiv: { - paddingLeft: 65, - }, - footerItemInnerDiv: { - paddingLeft: 24, - }, - borderedItem: { - borderBottomColor: colors.walletBorder, - borderBottomStyle: 'solid', - borderWidth: 1, - }, - tokenItem: { - backgroundColor: colors.walletDefaultItemBackground, - paddingTop: 8, - paddingBottom: 8, - }, - headerItem: { - paddingTop: 8, - paddingBottom: 8, - }, - wrappedEtherButtonLabel: { - fontSize: 12, - }, - amountLabel: { - fontWeight: 'bold', - color: colors.black, - }, -}; - -const ETHER_ICON_PATH = '/images/ether.png'; -const ETHER_TOKEN_SYMBOL = 'WETH'; -const ZRX_TOKEN_SYMBOL = 'ZRX'; -const ETHER_SYMBOL = 'ETH'; -const ICON_DIMENSION = 24; -const TOKEN_AMOUNT_DISPLAY_PRECISION = 3; - -export class Wallet extends React.Component { - private _isUnmounted: boolean; - constructor(props: WalletProps) { - super(props); - this._isUnmounted = false; - const initialTrackedTokenStateByAddress = this._getInitialTrackedTokenStateByAddress(props.trackedTokens); - this.state = { - trackedTokenStateByAddress: initialTrackedTokenStateByAddress, - }; - } - public componentWillMount() { - const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); - // tslint:disable-next-line:no-floating-promises - this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); - } - public componentWillUnmount() { - this._isUnmounted = true; - } - public componentWillReceiveProps(nextProps: WalletProps) { - if ( - nextProps.userAddress !== this.props.userAddress || - nextProps.networkId !== this.props.networkId || - nextProps.lastForceTokenStateRefetch !== this.props.lastForceTokenStateRefetch - ) { - const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); - // tslint:disable-next-line:no-floating-promises - this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); - } - if (!_.isEqual(nextProps.trackedTokens, this.props.trackedTokens)) { - const newTokens = _.difference(nextProps.trackedTokens, this.props.trackedTokens); - const newTokenAddresses = _.map(newTokens, token => token.address); - // Add placeholder entry for this token to the state, since fetching the - // balance/allowance is asynchronous - const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; - _.each(newTokenAddresses, (tokenAddress: string) => { - trackedTokenStateByAddress[tokenAddress] = { - balance: new BigNumber(0), - allowance: new BigNumber(0), - isLoaded: false, - }; - }); - this.setState({ - trackedTokenStateByAddress, - }); - // Fetch the actual balance/allowance. - // tslint:disable-next-line:no-floating-promises - this._fetchBalancesAndAllowancesAsync(newTokenAddresses); - } - } - public render() { - const isReadyToRender = this.props.blockchainIsLoaded && this.props.blockchainErr === BlockchainErrs.NoError; - return
{isReadyToRender && this._renderRows()}
; - } - private _renderRows() { - return ( - - {_.concat( - this._renderHeaderRows(), - this._renderEthRows(), - this._renderTokenRows(), - this._renderFooterRows(), - )} - - ); - } - private _renderHeaderRows() { - const userAddress = this.props.userAddress; - const primaryText = utils.getAddressBeginAndEnd(userAddress); - return ( - } - style={{ ...styles.headerItem, ...styles.borderedItem }} - innerDivStyle={styles.headerItemInnerDiv} - /> - ); - } - private _renderFooterRows() { - const primaryText = '+ other tokens'; - return ( - - ); - } - private _renderEthRows() { - const primaryText = this._renderAmount( - this.props.userEtherBalanceInWei, - constants.DECIMAL_PLACES_ETH, - ETHER_SYMBOL, - ); - const accessoryItemConfig = { - wrappedEtherAction: WrappedEtherAction.Wrap, - }; - return ( - } - rightAvatar={this._renderAccessoryItems(accessoryItemConfig)} - style={{ ...styles.tokenItem, ...styles.borderedItem }} - innerDivStyle={styles.tokenItemInnerDiv} - /> - ); - } - private _renderTokenRows() { - const trackedTokens = this.props.trackedTokens; - const trackedTokensStartingWithEtherToken = trackedTokens.sort( - firstBy((t: Token) => t.symbol !== ETHER_TOKEN_SYMBOL) - .thenBy((t: Token) => t.symbol !== ZRX_TOKEN_SYMBOL) - .thenBy('address'), - ); - return _.map(trackedTokensStartingWithEtherToken, this._renderTokenRow.bind(this)); - } - private _renderTokenRow(token: Token) { - const tokenState = this.state.trackedTokenStateByAddress[token.address]; - const tokenLink = sharedUtils.getEtherScanLinkIfExists( - token.address, - this.props.networkId, - EtherscanLinkSuffixes.Address, - ); - const amount = this._renderAmount(tokenState.balance, token.decimals, token.symbol); - const wrappedEtherAction = token.symbol === ETHER_TOKEN_SYMBOL ? WrappedEtherAction.Unwrap : undefined; - const accessoryItemConfig: AccessoryItemConfig = { - wrappedEtherAction, - allowanceToggleConfig: { - token, - tokenState, - }, - }; - return ( - - ); - } - private _renderAccessoryItems(config: AccessoryItemConfig) { - const shouldShowWrappedEtherAction = !_.isUndefined(config.wrappedEtherAction); - const shouldShowToggle = !_.isUndefined(config.allowanceToggleConfig); - return ( -
-
-
- {shouldShowWrappedEtherAction && this._renderWrappedEtherButton(config.wrappedEtherAction)} -
-
- {shouldShowToggle && this._renderAllowanceToggle(config.allowanceToggleConfig)} -
-
-
- ); - } - private _renderAllowanceToggle(config: AllowanceToggleConfig) { - return ( - - ); - } - private _renderAmount(amount: BigNumber, decimals: number, symbol: string) { - const unitAmount = ZeroEx.toUnitAmount(amount, decimals); - const formattedAmount = unitAmount.toPrecision(TOKEN_AMOUNT_DISPLAY_PRECISION); - const result = `${formattedAmount} ${symbol}`; - return
{result}
; - } - private _renderTokenIcon(token: Token, tokenLink?: string) { - const tooltipId = `tooltip-${token.address}`; - const tokenIcon = ; - if (_.isUndefined(tokenLink)) { - return tokenIcon; - } else { - return ( - - {tokenIcon} - - ); - } - } - private _renderWrappedEtherButton(action: WrappedEtherAction) { - let buttonLabel; - let buttonIcon; - switch (action) { - case WrappedEtherAction.Wrap: - buttonLabel = 'wrap'; - buttonIcon = ; - break; - case WrappedEtherAction.Unwrap: - buttonLabel = 'unwrap'; - buttonIcon = ; - break; - default: - throw utils.spawnSwitchErr('wrappedEtherAction', action); - } - return ( - - ); - } - private _getInitialTrackedTokenStateByAddress(trackedTokens: Token[]) { - const trackedTokenStateByAddress: TokenStateByAddress = {}; - _.each(trackedTokens, token => { - trackedTokenStateByAddress[token.address] = { - balance: new BigNumber(0), - allowance: new BigNumber(0), - isLoaded: false, - }; - }); - return trackedTokenStateByAddress; - } - private async _fetchBalancesAndAllowancesAsync(tokenAddresses: string[]) { - const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; - const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress; - for (const tokenAddress of tokenAddresses) { - const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( - userAddressIfExists, - tokenAddress, - ); - trackedTokenStateByAddress[tokenAddress] = { - balance, - allowance, - isLoaded: true, - }; - } - if (!this._isUnmounted) { - this.setState({ - trackedTokenStateByAddress, - }); - } - } - private async _refetchTokenStateAsync(tokenAddress: string) { - const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress; - const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( - userAddressIfExists, - tokenAddress, - ); - this.setState({ - trackedTokenStateByAddress: { - ...this.state.trackedTokenStateByAddress, - [tokenAddress]: { - balance, - allowance, - isLoaded: true, - }, - }, - }); - } -} diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx new file mode 100644 index 000000000..670dc07dd --- /dev/null +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -0,0 +1,443 @@ +import { ZeroEx } from '0x.js'; +import { + colors, + constants as sharedConstants, + EtherscanLinkSuffixes, + Styles, + utils as sharedUtils, +} from '@0xproject/react-shared'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +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'); + +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, 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; + blockchain: Blockchain; + blockchainIsLoaded: boolean; + blockchainErr: BlockchainErrs; + dispatcher: Dispatcher; + tokenByAddress: TokenByAddress; + trackedTokens: Token[]; + userEtherBalanceInWei: BigNumber; + lastForceTokenStateRefetch: number; +} + +interface WalletState { + trackedTokenStateByAddress: TokenStateByAddress; + wrappedEtherDirection?: Side; +} + +interface AllowanceToggleConfig { + token: Token; + tokenState: TokenState; +} + +interface AccessoryItemConfig { + wrappedEtherDirection?: Side; + allowanceToggleConfig?: AllowanceToggleConfig; +} + +const styles: Styles = { + wallet: { + width: 346, + backgroundColor: colors.white, + borderBottomRightRadius: 10, + borderBottomLeftRadius: 10, + borderTopRightRadius: 10, + borderTopLeftRadius: 10, + boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, + overflow: 'hidden', + }, + list: { + padding: 0, + }, + tokenItemInnerDiv: { + paddingLeft: 60, + }, + headerItemInnerDiv: { + paddingLeft: 65, + }, + footerItemInnerDiv: { + paddingLeft: 24, + }, + borderedItem: { + borderBottomColor: colors.walletBorder, + borderBottomStyle: 'solid', + borderWidth: 1, + }, + tokenItem: { + backgroundColor: colors.walletDefaultItemBackground, + }, + 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'; +const ETHER_TOKEN_SYMBOL = 'WETH'; +const ZRX_TOKEN_SYMBOL = 'ZRX'; +const ETHER_SYMBOL = 'ETH'; +const ICON_DIMENSION = 24; +const TOKEN_AMOUNT_DISPLAY_PRECISION = 3; + +export class Wallet extends React.Component { + private _isUnmounted: boolean; + constructor(props: WalletProps) { + super(props); + this._isUnmounted = false; + const initialTrackedTokenStateByAddress = this._getInitialTrackedTokenStateByAddress(props.trackedTokens); + this.state = { + trackedTokenStateByAddress: initialTrackedTokenStateByAddress, + wrappedEtherDirection: undefined, + }; + } + public componentWillMount() { + const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); + // tslint:disable-next-line:no-floating-promises + this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); + } + public componentWillUnmount() { + this._isUnmounted = true; + } + public componentWillReceiveProps(nextProps: WalletProps) { + if ( + nextProps.userAddress !== this.props.userAddress || + nextProps.networkId !== this.props.networkId || + nextProps.lastForceTokenStateRefetch !== this.props.lastForceTokenStateRefetch + ) { + const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); + // tslint:disable-next-line:no-floating-promises + this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); + } + if (!_.isEqual(nextProps.trackedTokens, this.props.trackedTokens)) { + const newTokens = _.difference(nextProps.trackedTokens, this.props.trackedTokens); + const newTokenAddresses = _.map(newTokens, token => token.address); + // Add placeholder entry for this token to the state, since fetching the + // balance/allowance is asynchronous + const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; + _.each(newTokenAddresses, (tokenAddress: string) => { + trackedTokenStateByAddress[tokenAddress] = { + balance: new BigNumber(0), + allowance: new BigNumber(0), + isLoaded: false, + }; + }); + this.setState({ + trackedTokenStateByAddress, + }); + // Fetch the actual balance/allowance. + // tslint:disable-next-line:no-floating-promises + this._fetchBalancesAndAllowancesAsync(newTokenAddresses); + } + } + public render() { + const isReadyToRender = this.props.blockchainIsLoaded && this.props.blockchainErr === BlockchainErrs.NoError; + return
{isReadyToRender && this._renderRows()}
; + } + private _renderRows() { + return ( + + {_.concat( + this._renderHeaderRows(), + this._renderEthRows(), + this._renderTokenRows(), + this._renderFooterRows(), + )} + + ); + } + private _renderHeaderRows() { + const userAddress = this.props.userAddress; + const primaryText = utils.getAddressBeginAndEnd(userAddress); + return ( + } + style={{ ...styles.paddedItem, ...styles.borderedItem }} + innerDivStyle={styles.headerItemInnerDiv} + /> + ); + } + private _renderFooterRows() { + const primaryText = '+ other tokens'; + return ; + } + private _renderEthRows() { + const primaryText = this._renderAmount( + this.props.userEtherBalanceInWei, + constants.DECIMAL_PLACES_ETH, + ETHER_SYMBOL, + ); + const accessoryItemConfig = { + 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 ( +
+ } + rightAvatar={this._renderAccessoryItems(accessoryItemConfig)} + disableTouchRipple={true} + style={style} + innerDivStyle={styles.tokenItemInnerDiv} + /> + {isInWrappedEtherState && ( + + )} +
+ ); + } + private _renderTokenRows() { + const trackedTokens = this.props.trackedTokens; + const trackedTokensStartingWithEtherToken = trackedTokens.sort( + firstBy((t: Token) => t.symbol !== ETHER_TOKEN_SYMBOL) + .thenBy((t: Token) => t.symbol !== ZRX_TOKEN_SYMBOL) + .thenBy('address'), + ); + return _.map(trackedTokensStartingWithEtherToken, this._renderTokenRow.bind(this)); + } + private _renderTokenRow(token: Token) { + const tokenState = this.state.trackedTokenStateByAddress[token.address]; + const tokenLink = sharedUtils.getEtherScanLinkIfExists( + token.address, + this.props.networkId, + EtherscanLinkSuffixes.Address, + ); + const amount = this._renderAmount(tokenState.balance, token.decimals, token.symbol); + const wrappedEtherDirection = token.symbol === ETHER_TOKEN_SYMBOL ? Side.Receive : undefined; + const accessoryItemConfig: AccessoryItemConfig = { + 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 ( +
+ + {shouldShowWrapEtherItem && ( + + )} +
+ ); + } + private _renderAccessoryItems(config: AccessoryItemConfig) { + const shouldShowWrappedEtherAction = !_.isUndefined(config.wrappedEtherDirection); + const shouldShowToggle = !_.isUndefined(config.allowanceToggleConfig); + return ( +
+
+
+ {shouldShowWrappedEtherAction && this._renderWrappedEtherButton(config.wrappedEtherDirection)} +
+
+ {shouldShowToggle && this._renderAllowanceToggle(config.allowanceToggleConfig)} +
+
+
+ ); + } + private _renderAllowanceToggle(config: AllowanceToggleConfig) { + return ( + + ); + } + private _renderAmount(amount: BigNumber, decimals: number, symbol: string) { + const unitAmount = ZeroEx.toUnitAmount(amount, decimals); + const formattedAmount = unitAmount.toPrecision(TOKEN_AMOUNT_DISPLAY_PRECISION); + const result = `${formattedAmount} ${symbol}`; + return
{result}
; + } + private _renderTokenIcon(token: Token, tokenLink?: string) { + const tooltipId = `tooltip-${token.address}`; + const tokenIcon = ; + if (_.isUndefined(tokenLink)) { + return tokenIcon; + } else { + return ( + + {tokenIcon} + + ); + } + } + private _renderWrappedEtherButton(wrappedEtherDirection: Side) { + const isWrappedEtherDirectionOpen = this.state.wrappedEtherDirection === wrappedEtherDirection; + let buttonLabel; + let buttonIcon; + if (isWrappedEtherDirectionOpen) { + buttonLabel = 'cancel'; + buttonIcon = ; + } else { + switch (wrappedEtherDirection) { + case Side.Deposit: + buttonLabel = 'wrap'; + buttonIcon = ; + break; + case Side.Receive: + buttonLabel = 'unwrap'; + buttonIcon = ; + break; + default: + throw utils.spawnSwitchErr('wrappedEtherDirection', wrappedEtherDirection); + } + } + const onClick = isWrappedEtherDirectionOpen + ? this._closeWrappedEtherActionRow.bind(this) + : this._openWrappedEtherActionRow.bind(this, wrappedEtherDirection); + return ( + + ); + } + private _getInitialTrackedTokenStateByAddress(trackedTokens: Token[]) { + const trackedTokenStateByAddress: TokenStateByAddress = {}; + _.each(trackedTokens, token => { + trackedTokenStateByAddress[token.address] = { + balance: new BigNumber(0), + allowance: new BigNumber(0), + isLoaded: false, + }; + }); + return trackedTokenStateByAddress; + } + private async _fetchBalancesAndAllowancesAsync(tokenAddresses: string[]) { + const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; + const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress; + for (const tokenAddress of tokenAddresses) { + const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( + userAddressIfExists, + tokenAddress, + ); + trackedTokenStateByAddress[tokenAddress] = { + balance, + allowance, + isLoaded: true, + }; + } + if (!this._isUnmounted) { + this.setState({ + trackedTokenStateByAddress, + }); + } + } + private async _refetchTokenStateAsync(tokenAddress: string) { + const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress; + const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( + userAddressIfExists, + tokenAddress, + ); + this.setState({ + trackedTokenStateByAddress: { + ...this.state.trackedTokenStateByAddress, + [tokenAddress]: { + balance, + allowance, + isLoaded: true, + }, + }, + }); + } + 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; + } +} diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx new file mode 100644 index 000000000..71ce6894f --- /dev/null +++ b/packages/website/ts/components/wallet/wrap_ether_item.tsx @@ -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; +} + +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 { + 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 ( + +
{topLabelText}
+
+ {isWrappingEth ? ( + + ) : ( + + )} +
+
+ } + 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 ( +
+ +
+ ); + } + private _renderWrapEtherConfirmationButton() { + const isWrappingEth = this.props.direction === Side.Deposit; + const labelText = isWrappingEth ? 'wrap' : 'unwrap'; + return ( +
+ +
+ ); + } + 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, + }); + } +} diff --git a/packages/website/ts/utils/mui_theme.ts b/packages/website/ts/utils/mui_theme.ts index 41bc2844b..d611f0895 100644 --- a/packages/website/ts/utils/mui_theme.ts +++ b/packages/website/ts/utils/mui_theme.ts @@ -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, - }, }); diff --git a/packages/website/ts/utils/wallet_item_styles.ts b/packages/website/ts/utils/wallet_item_styles.ts new file mode 100644 index 000000000..1ad304ce1 --- /dev/null +++ b/packages/website/ts/utils/wallet_item_styles.ts @@ -0,0 +1,7 @@ +import { colors, Styles } from '@0xproject/react-shared'; + +export const styles: Styles = { + focusedItem: { + backgroundColor: colors.walletFocusedItemBackground, + }, +}; -- cgit v1.2.3 From d106079d9b69191d9cdc6d9323dbae3e4b45daf2 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 22 Mar 2018 23:39:13 -0700 Subject: Remove props that are covered by the defaults --- packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx | 2 -- 1 file changed, 2 deletions(-) (limited to 'packages') 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 f61b6495e..d1bdb447f 100644 --- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx +++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx @@ -104,8 +104,6 @@ export class EthWethConversionDialog extends React.Component< onChange={this._onValueChange.bind(this)} amount={this.state.value} onVisitBalancesPageClick={this.props.onCancelled} - shouldShowErrs={true} - shouldShowUnderline={true} /> ) : ( Date: Thu, 29 Mar 2018 11:25:50 -0700 Subject: Style changes --- packages/website/ts/components/inputs/token_amount_input.tsx | 7 ++++++- packages/website/ts/components/wallet/wrap_ether_item.tsx | 7 +++---- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'packages') diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx index 591be7c0c..9e638b67b 100644 --- a/packages/website/ts/components/inputs/token_amount_input.tsx +++ b/packages/website/ts/components/inputs/token_amount_input.tsx @@ -33,6 +33,9 @@ interface TokenAmountInputState { isBalanceAndAllowanceLoaded: boolean; } +const HEIGHT_WITH_LABEL = 84; +const HEIGHT_WITHOUT_LABEL = 62; + export class TokenAmountInput extends React.Component { public static defaultProps: Partial = { shouldShowErrs: true, @@ -72,7 +75,9 @@ export class TokenAmountInput extends React.Component -- cgit v1.2.3 From 870da2ab225f37ae5b566e16ef2ae83834b3abf2 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Fri, 23 Mar 2018 16:29:10 -0700 Subject: Implement wallet locked and uninstalled states --- packages/website/public/images/metamask_icon.png | Bin 0 -> 5728 bytes packages/website/ts/components/portal.tsx | 3 + .../ts/components/top_bar/provider_display.tsx | 3 +- packages/website/ts/components/wallet/wallet.tsx | 54 +++++++++++--- .../components/wallet/wallet_disconnected_item.tsx | 79 +++++++++++++++++++++ 5 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 packages/website/public/images/metamask_icon.png create mode 100644 packages/website/ts/components/wallet/wallet_disconnected_item.tsx (limited to 'packages') diff --git a/packages/website/public/images/metamask_icon.png b/packages/website/public/images/metamask_icon.png new file mode 100644 index 000000000..ab497ecb7 Binary files /dev/null and b/packages/website/public/images/metamask_icon.png differ diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx index 55013e105..ceb0ecc72 100644 --- a/packages/website/ts/components/portal.tsx +++ b/packages/website/ts/components/portal.tsx @@ -305,6 +305,9 @@ export class Portal extends React.Component { trackedTokens={trackedTokens} userEtherBalanceInWei={this.props.userEtherBalanceInWei} lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} + injectedProviderName={this.props.injectedProviderName} + providerType={this.props.providerType} + onToggleLedgerDialog={this.onToggleLedgerDialog.bind(this)} /> diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx index 89c506d0e..221c34f8c 100644 --- a/packages/website/ts/components/top_bar/provider_display.tsx +++ b/packages/website/ts/components/top_bar/provider_display.tsx @@ -28,7 +28,8 @@ interface ProviderDisplayState {} export class ProviderDisplay extends React.Component { public render() { const isAddressAvailable = !_.isEmpty(this.props.userAddress); - const isExternallyInjectedProvider = ProviderType.Injected && this.props.injectedProviderName !== '0x Public'; + const isExternallyInjectedProvider = + this.props.providerType === ProviderType.Injected && this.props.injectedProviderName !== '0x Public'; const displayAddress = isAddressAvailable ? utils.getAddressBeginAndEnd(this.props.userAddress) : isExternallyInjectedProvider ? 'Account locked' : '0x0000...0000'; diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 670dc07dd..39c95d31c 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -10,6 +10,7 @@ import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import FlatButton from 'material-ui/FlatButton'; import { List, ListItem } from 'material-ui/List'; +import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet'; 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'; @@ -21,9 +22,19 @@ 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 { WalletDisconnectedItem } from 'ts/components/wallet/wallet_disconnected_item'; import { WrapEtherItem } from 'ts/components/wallet/wrap_ether_item'; import { Dispatcher } from 'ts/redux/dispatcher'; -import { BalanceErrs, BlockchainErrs, Side, Token, TokenByAddress, TokenState, TokenStateByAddress } from 'ts/types'; +import { + BalanceErrs, + BlockchainErrs, + ProviderType, + 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'; @@ -39,6 +50,9 @@ export interface WalletProps { trackedTokens: Token[]; userEtherBalanceInWei: BigNumber; lastForceTokenStateRefetch: number; + injectedProviderName: string; + providerType: ProviderType; + onToggleLedgerDialog: () => void; } interface WalletState { @@ -163,18 +177,42 @@ export class Wallet extends React.Component { return
{isReadyToRender && this._renderRows()}
; } private _renderRows() { + const isAddressAvailable = !_.isEmpty(this.props.userAddress); return ( - {_.concat( - this._renderHeaderRows(), - this._renderEthRows(), - this._renderTokenRows(), - this._renderFooterRows(), - )} + {isAddressAvailable + ? _.concat( + this._renderConnectedHeaderRows(), + this._renderEthRows(), + this._renderTokenRows(), + this._renderFooterRows(), + ) + : _.concat(this._renderDisconnectedHeaderRows(), this._renderDisconnectedRows())} ); } - private _renderHeaderRows() { + private _renderDisconnectedHeaderRows() { + const userAddress = this.props.userAddress; + const primaryText = 'wallet'; + return ( + } + style={styles.paddedItem} + innerDivStyle={styles.headerItemInnerDiv} + /> + ); + } + private _renderDisconnectedRows() { + return ( + + ); + } + private _renderConnectedHeaderRows() { const userAddress = this.props.userAddress; const primaryText = utils.getAddressBeginAndEnd(userAddress); return ( diff --git a/packages/website/ts/components/wallet/wallet_disconnected_item.tsx b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx new file mode 100644 index 000000000..d74ca23f6 --- /dev/null +++ b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx @@ -0,0 +1,79 @@ +import { colors, Styles } from '@0xproject/react-shared'; +import FlatButton from 'material-ui/FlatButton'; +import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet'; +import * as React from 'react'; + +import { ProviderType } from 'ts/types'; +import { constants } from 'ts/utils/constants'; + +export interface WalletDisconnectedItemProps { + providerType: ProviderType; + injectedProviderName: string; + onToggleLedgerDialog: () => void; +} + +const styles: Styles = { + button: { + border: colors.walletBorder, + borderStyle: 'solid', + borderWidth: 1, + height: 80, + }, + hrefAdjustment: { + paddingTop: 20, // For some reason when we set the href prop of a FlatButton material-ui reduces the top padding + }, +}; + +const ITEM_HEIGHT = 292; +const METAMASK_ICON_WIDTH = 35; +const LEDGER_ICON_WIDTH = 30; +const BUTTON_BOTTOM_PADDING = 80; + +export const WalletDisconnectedItem: React.StatelessComponent = ( + props: WalletDisconnectedItemProps, +) => { + const isExternallyInjectedProvider = + props.providerType === ProviderType.Injected && props.injectedProviderName !== '0x Public'; + return ( +
+
+
+
+ + +
+
+
+
+ ); +}; + +interface ProviderButtonProps { + isExternallyInjectedProvider: boolean; +} + +const ProviderButton: React.StatelessComponent = (props: ProviderButtonProps) => ( + } + style={props.isExternallyInjectedProvider ? styles.button : { ...styles.button, ...styles.hrefAdjustment }} + href={props.isExternallyInjectedProvider ? undefined : constants.URL_METAMASK_CHROME_STORE} + target={props.isExternallyInjectedProvider ? undefined : '_blank'} + disabled={props.isExternallyInjectedProvider} + /> +); -- cgit v1.2.3 From 57ca611e12d2e40c3f0f33023544a890be7ccb87 Mon Sep 17 00:00:00 2001 From: Ara Kevonian <=> Date: Fri, 30 Mar 2018 05:45:24 -0700 Subject: Monitor different state layers with OrderWatcher Allow instantiation of stand-alone OrderWatchers that can monitor different blockchain state layers (e.g. pending or latest) --- packages/0x.js/CHANGELOG.md | 3 + packages/0x.js/src/0x.ts | 28 +++---- packages/0x.js/src/order_watcher/event_watcher.ts | 8 +- .../0x.js/src/order_watcher/order_state_watcher.ts | 8 +- packages/0x.js/src/types.ts | 1 + packages/0x.js/test/order_state_watcher_test.ts | 89 ++++++++++++---------- 6 files changed, 77 insertions(+), 60 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md index ca6b985d3..ab656349f 100644 --- a/packages/0x.js/CHANGELOG.md +++ b/packages/0x.js/CHANGELOG.md @@ -3,6 +3,9 @@ ## v0.34.0 - _TBD_ * Fix the bug causing `zeroEx.exchange.fillOrdersUpToAsync` validation to fail if there were some extra orders passed (#470) + * Remove automatic instantiation of `zeroEx.orderStateWatcher` + * Add `zeroEx.createOrderStateWatcher` to allow creating arbitrary number of OrderStateWatchers + * Added `stateLayer` setting to `OrderStateWatcherConfig` so OrderStateWatcher can be set to monitor different blockchain state layers ## v0.33.2 - _March 18, 2018_ diff --git a/packages/0x.js/src/0x.ts b/packages/0x.js/src/0x.ts index 0dd728ff1..418d0ba34 100644 --- a/packages/0x.js/src/0x.ts +++ b/packages/0x.js/src/0x.ts @@ -15,7 +15,7 @@ import { OrderStateWatcher } from './order_watcher/order_state_watcher'; import { zeroExConfigSchema } from './schemas/zero_ex_config_schema'; import { zeroExPrivateNetworkConfigSchema } from './schemas/zero_ex_private_network_config_schema'; import { zeroExPublicNetworkConfigSchema } from './schemas/zero_ex_public_network_config_schema'; -import { Web3Provider, ZeroExConfig, ZeroExError } from './types'; +import { OrderStateWatcherConfig, ZeroExConfig, ZeroExError } from './types'; import { assert } from './utils/assert'; import { constants } from './utils/constants'; import { decorators } from './utils/decorators'; @@ -57,11 +57,6 @@ export class ZeroEx { * tokenTransferProxy smart contract. */ public proxy: TokenTransferProxyWrapper; - /** - * An instance of the OrderStateWatcher class containing methods for watching a set of orders for relevant - * blockchain state changes. - */ - public orderStateWatcher: OrderStateWatcher; private _web3Wrapper: Web3Wrapper; private _abiDecoder: AbiDecoder; /** @@ -197,13 +192,6 @@ export class ZeroEx { config.tokenRegistryContractAddress, ); this.etherToken = new EtherTokenWrapper(this._web3Wrapper, config.networkId, this._abiDecoder, this.token); - this.orderStateWatcher = new OrderStateWatcher( - this._web3Wrapper, - this._abiDecoder, - this.token, - this.exchange, - config.orderWatcherConfig, - ); } /** * Sets a new web3 provider for 0x.js. Updating the provider will stop all @@ -336,6 +324,20 @@ export class ZeroEx { const txReceipt = await txReceiptPromise; return txReceipt; } + /** + * Instantiates and returns a new OrderStateWatcher instance. + * @param config The configuration object. Look up the type for the description. + * @return An instance of the 0x.js OrderStateWatcher class. + */ + public createOrderStateWatcher(config?: OrderStateWatcherConfig) { + return new OrderStateWatcher( + this._web3Wrapper, + this._abiDecoder, + this.token, + this.exchange, + config, + ); + } /* * HACK: `TokenWrapper` needs a token transfer proxy address. `TokenTransferProxy` address is fetched from * an `ExchangeWrapper`. `ExchangeWrapper` needs `TokenWrapper` to validate orders, creating a dependency cycle. diff --git a/packages/0x.js/src/order_watcher/event_watcher.ts b/packages/0x.js/src/order_watcher/event_watcher.ts index 246ab8292..deb1ffbff 100644 --- a/packages/0x.js/src/order_watcher/event_watcher.ts +++ b/packages/0x.js/src/order_watcher/event_watcher.ts @@ -22,8 +22,10 @@ export class EventWatcher { private _pollingIntervalMs: number; private _intervalIdIfExists?: NodeJS.Timer; private _lastEvents: LogEntry[] = []; - constructor(web3Wrapper: Web3Wrapper, pollingIntervalIfExistsMs: undefined | number) { + private _stateLayer: BlockParamLiteral; + constructor(web3Wrapper: Web3Wrapper, pollingIntervalIfExistsMs: undefined | number, stateLayer: BlockParamLiteral = BlockParamLiteral.Pending) { this._web3Wrapper = web3Wrapper; + this._stateLayer = stateLayer; this._pollingIntervalMs = _.isUndefined(pollingIntervalIfExistsMs) ? DEFAULT_EVENT_POLLING_INTERVAL_MS : pollingIntervalIfExistsMs; @@ -69,8 +71,8 @@ export class EventWatcher { } private async _getEventsAsync(): Promise { const eventFilter = { - fromBlock: BlockParamLiteral.Pending, - toBlock: BlockParamLiteral.Pending, + fromBlock: this._stateLayer, + toBlock: this._stateLayer, }; const events = await this._web3Wrapper.getLogsAsync(eventFilter); return events; diff --git a/packages/0x.js/src/order_watcher/order_state_watcher.ts b/packages/0x.js/src/order_watcher/order_state_watcher.ts index 9cccadb7f..fcf3c351d 100644 --- a/packages/0x.js/src/order_watcher/order_state_watcher.ts +++ b/packages/0x.js/src/order_watcher/order_state_watcher.ts @@ -76,6 +76,7 @@ export class OrderStateWatcher { private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; private _cleanupJobInterval: number; private _cleanupJobIntervalIdIfExists?: NodeJS.Timer; + private _stateLayer: BlockParamLiteral; constructor( web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder, @@ -86,10 +87,13 @@ export class OrderStateWatcher { this._abiDecoder = abiDecoder; this._web3Wrapper = web3Wrapper; const pollingIntervalIfExistsMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs; - this._eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalIfExistsMs); + this._stateLayer = _.isUndefined(config) || _.isUndefined(config.stateLayer) + ? BlockParamLiteral.Pending + : config.stateLayer; + this._eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalIfExistsMs, this._stateLayer); this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( token, - BlockParamLiteral.Pending, + this._stateLayer, ); this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(exchange); this._orderStateUtils = new OrderStateUtils( diff --git a/packages/0x.js/src/types.ts b/packages/0x.js/src/types.ts index 38cfb6306..a13004720 100644 --- a/packages/0x.js/src/types.ts +++ b/packages/0x.js/src/types.ts @@ -175,6 +175,7 @@ export interface OrderStateWatcherConfig { eventPollingIntervalMs?: number; expirationMarginMs?: number; cleanupJobIntervalMs?: number; + stateLayer: BlockParamLiteral; } /* diff --git a/packages/0x.js/test/order_state_watcher_test.ts b/packages/0x.js/test/order_state_watcher_test.ts index d08272c3b..4f727d495 100644 --- a/packages/0x.js/test/order_state_watcher_test.ts +++ b/packages/0x.js/test/order_state_watcher_test.ts @@ -15,6 +15,9 @@ import { ZeroEx, ZeroExError, } from '../src'; +import { + OrderStateWatcher, +} from '../src/order_watcher/order_state_watcher'; import { DoneCallback } from '../src/types'; import { chaiSetup } from './utils/chai_setup'; @@ -43,6 +46,7 @@ describe('OrderStateWatcher', () => { let maker: string; let taker: string; let signedOrder: SignedOrder; + let orderStateWatcher: OrderStateWatcher; const config = { networkId: constants.TESTRPC_NETWORK_ID, }; @@ -50,6 +54,7 @@ describe('OrderStateWatcher', () => { const fillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals); before(async () => { zeroEx = new ZeroEx(web3.currentProvider, config); + orderStateWatcher = zeroEx.createOrderStateWatcher(); exchangeContractAddress = zeroEx.exchange.getContractAddress(); userAddresses = await zeroEx.getAvailableAddressesAsync(); [, maker, taker] = userAddresses; @@ -76,17 +81,17 @@ describe('OrderStateWatcher', () => { fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - zeroEx.orderStateWatcher.addOrder(signedOrder); - expect((zeroEx.orderStateWatcher as any)._orderByOrderHash).to.include({ + orderStateWatcher.addOrder(signedOrder); + expect((orderStateWatcher as any)._orderByOrderHash).to.include({ [orderHash]: signedOrder, }); - let dependentOrderHashes = (zeroEx.orderStateWatcher as any)._dependentOrderHashes; + let dependentOrderHashes = (orderStateWatcher as any)._dependentOrderHashes; expect(dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress]).to.have.keys(orderHash); - zeroEx.orderStateWatcher.removeOrder(orderHash); - expect((zeroEx.orderStateWatcher as any)._orderByOrderHash).to.not.include({ + orderStateWatcher.removeOrder(orderHash); + expect((orderStateWatcher as any)._orderByOrderHash).to.not.include({ [orderHash]: signedOrder, }); - dependentOrderHashes = (zeroEx.orderStateWatcher as any)._dependentOrderHashes; + dependentOrderHashes = (orderStateWatcher as any)._dependentOrderHashes; expect(dependentOrderHashes[signedOrder.maker]).to.be.undefined(); }); it('should no-op when removing a non-existing order', async () => { @@ -103,23 +108,23 @@ describe('OrderStateWatcher', () => { .split('') .reverse() .join('')}`; - zeroEx.orderStateWatcher.removeOrder(nonExistentOrderHash); + orderStateWatcher.removeOrder(nonExistentOrderHash); }); }); describe('#subscribe', async () => { afterEach(async () => { - zeroEx.orderStateWatcher.unsubscribe(); + orderStateWatcher.unsubscribe(); }); it('should fail when trying to subscribe twice', async () => { - zeroEx.orderStateWatcher.subscribe(_.noop); - expect(() => zeroEx.orderStateWatcher.subscribe(_.noop)).to.throw(ZeroExError.SubscriptionAlreadyPresent); + orderStateWatcher.subscribe(_.noop); + expect(() => orderStateWatcher.subscribe(_.noop)).to.throw(ZeroExError.SubscriptionAlreadyPresent); }); }); describe('tests with cleanup', async () => { afterEach(async () => { - zeroEx.orderStateWatcher.unsubscribe(); + orderStateWatcher.unsubscribe(); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - zeroEx.orderStateWatcher.removeOrder(orderHash); + orderStateWatcher.removeOrder(orderHash); }); it('should emit orderStateInvalid when maker allowance set to 0 for watched order', (done: DoneCallback) => { (async () => { @@ -131,14 +136,14 @@ describe('OrderStateWatcher', () => { fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.false(); const invalidOrderState = orderState as OrderStateInvalid; expect(invalidOrderState.orderHash).to.be.equal(orderHash); expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0)); })().catch(done); }); @@ -151,11 +156,11 @@ describe('OrderStateWatcher', () => { taker, fillableAmount, ); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { throw new Error('OrderState callback fired for irrelevant order'); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); const notTheMaker = userAddresses[0]; const anyRecipient = taker; const transferAmount = new BigNumber(2); @@ -175,14 +180,14 @@ describe('OrderStateWatcher', () => { fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.false(); const invalidOrderState = orderState as OrderStateInvalid; expect(invalidOrderState.orderHash).to.be.equal(orderHash); expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); const anyRecipient = taker; const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); await zeroEx.token.transferAsync(makerToken.address, maker, anyRecipient, makerBalance); @@ -198,7 +203,7 @@ describe('OrderStateWatcher', () => { fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.false(); @@ -206,7 +211,7 @@ describe('OrderStateWatcher', () => { expect(invalidOrderState.orderHash).to.be.equal(orderHash); expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); const shouldThrowOnInsufficientBalanceOrAllowance = true; await zeroEx.exchange.fillOrderAsync( @@ -230,7 +235,7 @@ describe('OrderStateWatcher', () => { const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); const fillAmountInBaseUnits = new BigNumber(2); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.true(); @@ -247,7 +252,7 @@ describe('OrderStateWatcher', () => { ); expect(orderRelevantState.makerBalance).to.be.bignumber.equal(remainingMakerBalance); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); const shouldThrowOnInsufficientBalanceOrAllowance = true; await zeroEx.exchange.fillOrderAsync( signedOrder, @@ -272,8 +277,8 @@ describe('OrderStateWatcher', () => { taker, ); const callback = reportNodeCallbackErrors(done)(); - zeroEx.orderStateWatcher.addOrder(signedOrder); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.subscribe(callback); await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, new BigNumber(0)); })().catch(done); }); @@ -292,7 +297,7 @@ describe('OrderStateWatcher', () => { ); const fillAmountInBaseUnits = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.true(); const validOrderState = orderState as OrderStateValid; @@ -305,7 +310,7 @@ describe('OrderStateWatcher', () => { ZeroEx.toBaseUnitAmount(new BigNumber(8), decimals), ); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); const shouldThrowOnInsufficientBalanceOrAllowance = true; await zeroEx.exchange.fillOrderAsync( signedOrder, @@ -326,7 +331,7 @@ describe('OrderStateWatcher', () => { ); const changedMakerApprovalAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { const validOrderState = orderState as OrderStateValid; @@ -338,7 +343,7 @@ describe('OrderStateWatcher', () => { changedMakerApprovalAmount, ); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, changedMakerApprovalAmount); })().catch(done); }); @@ -356,7 +361,7 @@ describe('OrderStateWatcher', () => { const remainingAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals); const transferAmount = makerBalance.sub(remainingAmount); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.true(); @@ -369,7 +374,7 @@ describe('OrderStateWatcher', () => { remainingAmount, ); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); await zeroEx.token.transferAsync(makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferAmount); })().catch(done); }); @@ -391,7 +396,7 @@ describe('OrderStateWatcher', () => { const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals); const transferTokenAmount = makerFee.sub(remainingTokenAmount); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.true(); @@ -401,7 +406,7 @@ describe('OrderStateWatcher', () => { remainingTokenAmount, ); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); await zeroEx.exchange.cancelOrderAsync(signedOrder, transferTokenAmount); })().catch(done); }); @@ -425,7 +430,7 @@ describe('OrderStateWatcher', () => { const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals); const transferTokenAmount = makerFee.sub(remainingTokenAmount); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { const validOrderState = orderState as OrderStateValid; @@ -434,7 +439,7 @@ describe('OrderStateWatcher', () => { remainingFeeAmount, ); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, remainingFeeAmount); await zeroEx.token.transferAsync( makerToken.address, @@ -460,7 +465,7 @@ describe('OrderStateWatcher', () => { feeRecipient, ); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { const validOrderState = orderState as OrderStateValid; @@ -469,7 +474,7 @@ describe('OrderStateWatcher', () => { fillableAmount, ); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); await zeroEx.token.setProxyAllowanceAsync( makerToken.address, maker, @@ -488,7 +493,7 @@ describe('OrderStateWatcher', () => { fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.false(); @@ -496,7 +501,7 @@ describe('OrderStateWatcher', () => { expect(invalidOrderState.orderHash).to.be.equal(orderHash); expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); })().catch(done); @@ -512,7 +517,7 @@ describe('OrderStateWatcher', () => { fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.false(); @@ -520,7 +525,7 @@ describe('OrderStateWatcher', () => { expect(invalidOrderState.orderHash).to.be.equal(orderHash); expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderFillRoundingError); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); await zeroEx.exchange.cancelOrderAsync( signedOrder, fillableAmount.minus(remainingFillableAmountInBaseUnits), @@ -539,7 +544,7 @@ describe('OrderStateWatcher', () => { const cancelAmountInBaseUnits = new BigNumber(2); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - zeroEx.orderStateWatcher.addOrder(signedOrder); + orderStateWatcher.addOrder(signedOrder); const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.true(); @@ -548,7 +553,7 @@ describe('OrderStateWatcher', () => { const orderRelevantState = validOrderState.orderRelevantState; expect(orderRelevantState.cancelledTakerTokenAmount).to.be.bignumber.equal(cancelAmountInBaseUnits); }); - zeroEx.orderStateWatcher.subscribe(callback); + orderStateWatcher.subscribe(callback); await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmountInBaseUnits); })().catch(done); }); -- cgit v1.2.3 From a322148631e910466d1a08dce55b5e46e76f8863 Mon Sep 17 00:00:00 2001 From: Ara Kevonian <=> Date: Fri, 30 Mar 2018 08:00:05 -0700 Subject: Amended comments to clarify changes on PR #488 --- packages/0x.js/CHANGELOG.md | 6 +++--- packages/0x.js/src/0x.ts | 1 + packages/0x.js/src/types.ts | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md index ab656349f..af63118a7 100644 --- a/packages/0x.js/CHANGELOG.md +++ b/packages/0x.js/CHANGELOG.md @@ -3,9 +3,9 @@ ## v0.34.0 - _TBD_ * Fix the bug causing `zeroEx.exchange.fillOrdersUpToAsync` validation to fail if there were some extra orders passed (#470) - * Remove automatic instantiation of `zeroEx.orderStateWatcher` - * Add `zeroEx.createOrderStateWatcher` to allow creating arbitrary number of OrderStateWatchers - * Added `stateLayer` setting to `OrderStateWatcherConfig` so OrderStateWatcher can be set to monitor different blockchain state layers + * Remove automatic instantiation of `zeroEx.orderStateWatcher` (#488) + * Add `zeroEx.createOrderStateWatcher` to allow creating arbitrary number of OrderStateWatchers (#488) + * Added `stateLayer` setting to `OrderStateWatcherConfig` so OrderStateWatcher can be set to monitor different blockchain state layers (#488) ## v0.33.2 - _March 18, 2018_ diff --git a/packages/0x.js/src/0x.ts b/packages/0x.js/src/0x.ts index 418d0ba34..7627f1d6e 100644 --- a/packages/0x.js/src/0x.ts +++ b/packages/0x.js/src/0x.ts @@ -326,6 +326,7 @@ export class ZeroEx { } /** * Instantiates and returns a new OrderStateWatcher instance. + * Defaults to watching the pending state. * @param config The configuration object. Look up the type for the description. * @return An instance of the 0x.js OrderStateWatcher class. */ diff --git a/packages/0x.js/src/types.ts b/packages/0x.js/src/types.ts index a13004720..8dede4b50 100644 --- a/packages/0x.js/src/types.ts +++ b/packages/0x.js/src/types.ts @@ -169,6 +169,7 @@ export type Web3Provider = Web3.Provider; * expirationMarginMs: Amount of time before order expiry that you'd like to be notified * of an orders expiration. Defaults: 0 * cleanupJobIntervalMs: How often to run a cleanup job which revalidates all the orders. Defaults: 1h + * stateLayer: Optional blockchain state layer OrderWatcher will monitor for new events. Defaults: pending */ export interface OrderStateWatcherConfig { orderExpirationCheckingIntervalMs?: number; -- cgit v1.2.3 From 3e285c0ef109c380f5fcc872555d4cdaeee79384 Mon Sep 17 00:00:00 2001 From: Ara Kevonian <=> Date: Fri, 30 Mar 2018 09:59:57 -0700 Subject: OrderStateWatcher fixes for PR #488 --- packages/0x.js/src/order_watcher/event_watcher.ts | 2 +- packages/0x.js/src/order_watcher/order_state_watcher.ts | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/src/order_watcher/event_watcher.ts b/packages/0x.js/src/order_watcher/event_watcher.ts index deb1ffbff..d01542a8c 100644 --- a/packages/0x.js/src/order_watcher/event_watcher.ts +++ b/packages/0x.js/src/order_watcher/event_watcher.ts @@ -23,7 +23,7 @@ export class EventWatcher { private _intervalIdIfExists?: NodeJS.Timer; private _lastEvents: LogEntry[] = []; private _stateLayer: BlockParamLiteral; - constructor(web3Wrapper: Web3Wrapper, pollingIntervalIfExistsMs: undefined | number, stateLayer: BlockParamLiteral = BlockParamLiteral.Pending) { + constructor(web3Wrapper: Web3Wrapper, pollingIntervalIfExistsMs: undefined | number, stateLayer: BlockParamLiteral = BlockParamLiteral.Latest) { this._web3Wrapper = web3Wrapper; this._stateLayer = stateLayer; this._pollingIntervalMs = _.isUndefined(pollingIntervalIfExistsMs) diff --git a/packages/0x.js/src/order_watcher/order_state_watcher.ts b/packages/0x.js/src/order_watcher/order_state_watcher.ts index fcf3c351d..bfd250e21 100644 --- a/packages/0x.js/src/order_watcher/order_state_watcher.ts +++ b/packages/0x.js/src/order_watcher/order_state_watcher.ts @@ -76,7 +76,6 @@ export class OrderStateWatcher { private _balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; private _cleanupJobInterval: number; private _cleanupJobIntervalIdIfExists?: NodeJS.Timer; - private _stateLayer: BlockParamLiteral; constructor( web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder, @@ -87,13 +86,13 @@ export class OrderStateWatcher { this._abiDecoder = abiDecoder; this._web3Wrapper = web3Wrapper; const pollingIntervalIfExistsMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs; - this._stateLayer = _.isUndefined(config) || _.isUndefined(config.stateLayer) - ? BlockParamLiteral.Pending + const stateLayer = _.isUndefined(config) || _.isUndefined(config.stateLayer) + ? BlockParamLiteral.Latest : config.stateLayer; - this._eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalIfExistsMs, this._stateLayer); + this._eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalIfExistsMs, stateLayer); this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( token, - this._stateLayer, + stateLayer, ); this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(exchange); this._orderStateUtils = new OrderStateUtils( -- cgit v1.2.3 From 57446d78f14fa1a2ae4d64e4c470095a01c7396e Mon Sep 17 00:00:00 2001 From: Ara Kevonian <=> Date: Fri, 30 Mar 2018 10:28:13 -0700 Subject: Fixed comment(s) for PR #488 --- packages/0x.js/src/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/src/types.ts b/packages/0x.js/src/types.ts index 8dede4b50..7f1f140ca 100644 --- a/packages/0x.js/src/types.ts +++ b/packages/0x.js/src/types.ts @@ -165,11 +165,11 @@ export type Web3Provider = Web3.Provider; /* * orderExpirationCheckingIntervalMs: How often to check for expired orders. Default: 50 - * eventPollingIntervalMs: How often to poll the Ethereum node for new events. Defaults: 200 + * eventPollingIntervalMs: How often to poll the Ethereum node for new events. Default: 200 * expirationMarginMs: Amount of time before order expiry that you'd like to be notified - * of an orders expiration. Defaults: 0 + * of an orders expiration. Default: 0 * cleanupJobIntervalMs: How often to run a cleanup job which revalidates all the orders. Defaults: 1h - * stateLayer: Optional blockchain state layer OrderWatcher will monitor for new events. Defaults: pending + * stateLayer: Optional blockchain state layer OrderWatcher will monitor for new events. Default: latest */ export interface OrderStateWatcherConfig { orderExpirationCheckingIntervalMs?: number; -- cgit v1.2.3 From cdbb3a015fec03f84524fd9072e8e73f6e6fbc53 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Fri, 30 Mar 2018 15:00:02 -0700 Subject: Add HACK comment and reorganize use other wallet button --- .../components/wallet/wallet_disconnected_item.tsx | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'packages') diff --git a/packages/website/ts/components/wallet/wallet_disconnected_item.tsx b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx index d74ca23f6..89e32f7be 100644 --- a/packages/website/ts/components/wallet/wallet_disconnected_item.tsx +++ b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx @@ -20,7 +20,12 @@ const styles: Styles = { height: 80, }, hrefAdjustment: { - paddingTop: 20, // For some reason when we set the href prop of a FlatButton material-ui reduces the top padding + paddingTop: 20, // HACK: For some reason when we set the href prop of a FlatButton material-ui reduces the top padding + }, + otherWalletText: { + fontSize: 14, + color: colors.grey500, + textDecoration: 'underline', }, }; @@ -42,15 +47,12 @@ export const WalletDisconnectedItem: React.StatelessComponent
- - - use other wallet - +
+ + + user other wallet + +
-- cgit v1.2.3