diff options
Diffstat (limited to 'packages/website/ts/components/wallet')
6 files changed, 0 insertions, 1003 deletions
diff --git a/packages/website/ts/components/wallet/body_overlay.tsx b/packages/website/ts/components/wallet/body_overlay.tsx deleted file mode 100644 index 3795f0eaa..000000000 --- a/packages/website/ts/components/wallet/body_overlay.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import * as _ from 'lodash'; -import * as React from 'react'; - -import { Blockchain } from 'ts/blockchain'; -import { Container } from 'ts/components/ui/container'; -import { Image } from 'ts/components/ui/image'; -import { Island } from 'ts/components/ui/island'; -import { Text } from 'ts/components/ui/text'; -import { Dispatcher } from 'ts/redux/dispatcher'; -import { colors } from 'ts/style/colors'; -import { styled } from 'ts/style/theme'; -import { AccountState, ProviderType } from 'ts/types'; -import { utils } from 'ts/utils/utils'; - -const METAMASK_IMG_SRC = '/images/metamask_icon.png'; -const COINBASE_WALLET_IMG_SRC = '/images/coinbase_wallet_logo.png'; - -export interface BodyOverlayProps { - dispatcher: Dispatcher; - userAddress: string; - injectedProviderName: string; - providerType: ProviderType; - onToggleLedgerDialog: () => void; - blockchain?: Blockchain; - blockchainIsLoaded: boolean; -} - -interface BodyOverlayState {} - -export class BodyOverlay extends React.Component<BodyOverlayProps, BodyOverlayState> { - public render(): React.ReactNode { - const accountState = this._getAccountState(); - switch (accountState) { - case AccountState.Locked: - return <LockedOverlay onUseDifferentWalletClicked={this.props.onToggleLedgerDialog} />; - case AccountState.Disconnected: - return <DisconnectedOverlay onUseDifferentWalletClicked={this.props.onToggleLedgerDialog} />; - case AccountState.Ready: - case AccountState.Loading: - default: - return null; - } - } - private _isBlockchainReady(): boolean { - return this.props.blockchainIsLoaded && !_.isUndefined(this.props.blockchain); - } - private _getAccountState(): AccountState { - return utils.getAccountState( - this._isBlockchainReady(), - this.props.providerType, - this.props.injectedProviderName, - this.props.userAddress, - ); - } -} - -interface LockedOverlayProps { - className?: string; - onUseDifferentWalletClicked?: () => void; -} -const PlainLockedOverlay: React.StatelessComponent<LockedOverlayProps> = ({ - className, - onUseDifferentWalletClicked, -}) => ( - <div className={className}> - <Container - className="flex flex-column items-center" - marginBottom="24px" - marginTop="24px" - marginLeft="48px" - marginRight="48px" - > - <Image src={METAMASK_IMG_SRC} height="70px" /> - <Container marginTop="12px"> - <Text fontColor={colors.metaMaskOrange} fontSize="16px" fontWeight="bold"> - Please Unlock MetaMask - </Text> - </Container> - <UseDifferentWallet fontColor={colors.darkGrey} onClick={onUseDifferentWalletClicked} /> - </Container> - </div> -); -const LockedOverlay = styled(PlainLockedOverlay)` - background: ${colors.metaMaskTransparentOrange}; - border: 1px solid ${colors.metaMaskOrange}; - border-radius: 10px; -`; - -interface DisconnectedOverlayProps { - onUseDifferentWalletClicked?: () => void; -} -const DisconnectedOverlay = (props: DisconnectedOverlayProps) => { - return ( - <div className="flex flex-column items-center"> - <GetWalletCallToAction /> - {!utils.isMobileOperatingSystem() && ( - <UseDifferentWallet fontColor={colors.mediumBlue} onClick={props.onUseDifferentWalletClicked} /> - )} - </div> - ); -}; - -interface UseDifferentWallet { - fontColor: string; - onClick?: () => void; -} -const UseDifferentWallet = (props: UseDifferentWallet) => { - return ( - <Container marginTop="12px"> - <Text fontColor={props.fontColor} fontSize="16px" textDecorationLine="underline" onClick={props.onClick}> - Use a different wallet - </Text> - </Container> - ); -}; - -const GetWalletCallToAction = () => { - const [downloadLink, isOnMobile] = utils.getBestWalletDownloadLinkAndIsMobile(); - const imageUrl = isOnMobile ? COINBASE_WALLET_IMG_SRC : METAMASK_IMG_SRC; - const text = isOnMobile ? 'Get Coinbase Wallet' : 'Get MetaMask Wallet'; - return ( - <a href={downloadLink} target="_blank" style={{ textDecoration: 'none' }}> - <Island - className="flex items-center py1 px2" - style={{ height: 28, borderRadius: 28, backgroundColor: colors.mediumBlue }} - > - <Image src={imageUrl} width="28px" borderRadius="22%" /> - <Container marginLeft="8px" marginRight="12px"> - <Text fontColor={colors.white} fontSize="16px" fontWeight={500}> - {text} - </Text> - </Container> - </Island> - </a> - ); -}; diff --git a/packages/website/ts/components/wallet/null_token_row.tsx b/packages/website/ts/components/wallet/null_token_row.tsx deleted file mode 100644 index a1ec9871a..000000000 --- a/packages/website/ts/components/wallet/null_token_row.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from 'react'; - -import { Circle } from 'ts/components/ui/circle'; -import { Container } from 'ts/components/ui/container'; -import { Text } from 'ts/components/ui/text'; -import { PlaceHolder } from 'ts/components/wallet/placeholder'; -import { StandardIconRow } from 'ts/components/wallet/standard_icon_row'; -import { colors } from 'ts/style/colors'; - -export interface NullTokenRowProps { - iconDimension: number; - fillColor: string; -} - -export const NullTokenRow: React.StatelessComponent<NullTokenRowProps> = ({ iconDimension, fillColor }) => { - const icon = <Circle diameter={iconDimension} fillColor={fillColor} />; - const main = ( - <div className="flex flex-column"> - <PlaceHolder hideChildren={true} fillColor={fillColor}> - <Text fontSize="16px" fontWeight="bold" lineHeight="1em"> - 0.00 XXX - </Text> - </PlaceHolder> - <Container marginTop="3px"> - <PlaceHolder hideChildren={true} fillColor={fillColor}> - <Text fontSize="14px" fontColor={colors.darkGrey} lineHeight="1em"> - $0.00 - </Text> - </PlaceHolder> - </Container> - </div> - ); - const accessory = ( - <Container marginRight="12px"> - <PlaceHolder hideChildren={true} fillColor={fillColor}> - <Container width="20px" height="14px" /> - </PlaceHolder> - </Container> - ); - return <StandardIconRow icon={icon} main={main} accessory={accessory} />; -}; diff --git a/packages/website/ts/components/wallet/placeholder.tsx b/packages/website/ts/components/wallet/placeholder.tsx deleted file mode 100644 index bf40d2ea8..000000000 --- a/packages/website/ts/components/wallet/placeholder.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from 'react'; - -import { styled } from 'ts/style/theme'; - -export interface PlaceHolderProps { - className?: string; - hideChildren: React.ReactNode; - fillColor: string; -} - -const PlainPlaceHolder: React.StatelessComponent<PlaceHolderProps> = ({ className, hideChildren, children }) => { - const childrenVisibility = hideChildren ? 'hidden' : 'visible'; - const childrenStyle: React.CSSProperties = { visibility: childrenVisibility }; - return ( - <div className={className}> - <div style={childrenStyle}>{children}</div> - </div> - ); -}; - -export const PlaceHolder = styled(PlainPlaceHolder)` - background-color: ${props => (props.hideChildren ? props.fillColor : 'transparent')}; - display: inline-block; - border-radius: 2px; -`; diff --git a/packages/website/ts/components/wallet/standard_icon_row.tsx b/packages/website/ts/components/wallet/standard_icon_row.tsx deleted file mode 100644 index 1a2ec021b..000000000 --- a/packages/website/ts/components/wallet/standard_icon_row.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import * as React from 'react'; - -import { colors } from 'ts/style/colors'; -import { styled } from 'ts/style/theme'; - -export interface StandardIconRowProps { - className?: string; - icon: React.ReactNode; - main: React.ReactNode; - accessory?: React.ReactNode; - minHeight?: string; - borderBottomColor?: string; - borderBottomStyle?: string; - borderWidth?: string; - backgroundColor?: string; -} -const PlainStandardIconRow: React.StatelessComponent<StandardIconRowProps> = ({ className, icon, main, accessory }) => { - return ( - <div className={`flex items-center ${className}`}> - <div className="flex items-center px2">{icon}</div> - <div className="flex-none pr2">{main}</div> - <div className="flex-auto" /> - <div>{accessory}</div> - </div> - ); -}; - -export const StandardIconRow = styled(PlainStandardIconRow)` - min-height: ${props => props.minHeight}; - border-bottom-color: ${props => props.borderBottomColor}; - border-bottom-style: ${props => props.borderBottomStyle}; - border-width: ${props => props.borderWidth}; - background-color: ${props => props.backgroundColor}; -`; - -StandardIconRow.defaultProps = { - minHeight: '85px', - borderBottomColor: colors.walletBorder, - borderBottomStyle: 'solid', - borderWidth: '1px', - backgroundColor: colors.walletDefaultItemBackground, -}; - -StandardIconRow.displayName = 'StandardIconRow'; diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx deleted file mode 100644 index d9da0b9d5..000000000 --- a/packages/website/ts/components/wallet/wallet.tsx +++ /dev/null @@ -1,527 +0,0 @@ -import { EtherscanLinkSuffixes, utils as sharedUtils } from '@0x/react-shared'; -import { BigNumber, errorUtils } from '@0x/utils'; -import * as _ from 'lodash'; - -import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet'; -import * as React from 'react'; -import firstBy from 'thenby'; - -import { Blockchain } from 'ts/blockchain'; -import { AccountConnection } from 'ts/components/ui/account_connection'; -import { Balance } from 'ts/components/ui/balance'; -import { Container } from 'ts/components/ui/container'; -import { DropDown, DropdownMouseEvent } from 'ts/components/ui/drop_down'; -import { IconButton } from 'ts/components/ui/icon_button'; -import { Identicon } from 'ts/components/ui/identicon'; -import { Island } from 'ts/components/ui/island'; -import { PointerDirection } from 'ts/components/ui/pointer'; -import { - CopyAddressSimpleMenuItem, - DifferentWalletSimpleMenuItem, - GoToAccountManagementSimpleMenuItem, - SimpleMenu, - SimpleMenuItem, -} from 'ts/components/ui/simple_menu'; -import { Text } from 'ts/components/ui/text'; -import { TokenIcon } from 'ts/components/ui/token_icon'; -import { BodyOverlay } from 'ts/components/wallet/body_overlay'; -import { NullTokenRow } from 'ts/components/wallet/null_token_row'; -import { PlaceHolder } from 'ts/components/wallet/placeholder'; -import { StandardIconRow } from 'ts/components/wallet/standard_icon_row'; -import { WrapEtherItem } from 'ts/components/wallet/wrap_ether_item'; -import { AllowanceStateToggle } from 'ts/containers/inputs/allowance_state_toggle'; -import { Dispatcher } from 'ts/redux/dispatcher'; -import { colors } from 'ts/style/colors'; -import { - AccountState, - BlockchainErrs, - ProviderType, - ScreenWidths, - Side, - Token, - TokenByAddress, - TokenState, - TokenStateByAddress, -} from 'ts/types'; -import { analytics } from 'ts/utils/analytics'; -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; - injectedProviderName: string; - providerType: ProviderType; - screenWidth: ScreenWidths; - location: Location; - trackedTokenStateByAddress: TokenStateByAddress; - onToggleLedgerDialog: () => void; - onAddToken: () => void; - onRemoveToken: () => void; - refetchTokenStateAsync: (tokenAddress: string) => Promise<void>; - style: React.CSSProperties; - toggleTooltipDirection?: PointerDirection; -} - -interface WalletState { - wrappedEtherDirection?: Side; - isHoveringSidebar: boolean; -} - -interface AllowanceStateToggleConfig { - token: Token; - tokenState: TokenState; -} - -interface AccessoryItemConfig { - wrappedEtherDirection?: Side; - allowanceStateToggleConfig?: AllowanceStateToggleConfig; -} - -const ETHER_ICON_PATH = '/images/ether.png'; -const ICON_DIMENSION = 28; -const BODY_ITEM_KEY = 'BODY'; -const HEADER_ITEM_KEY = 'HEADER'; -const ETHER_ITEM_KEY = 'ETHER'; -const WRAP_ROW_ALLOWANCE_TOGGLE_WIDTH = 67; -const ALLOWANCE_TOGGLE_WIDTH = 56; -const PLACEHOLDER_COLOR = colors.grey300; -const LOADING_ROWS_COUNT = 6; - -export class Wallet extends React.Component<WalletProps, WalletState> { - public static defaultProps = { - style: {}, - }; - constructor(props: WalletProps) { - super(props); - this.state = { - wrappedEtherDirection: undefined, - isHoveringSidebar: false, - }; - } - public componentDidUpdate(prevProps: WalletProps): void { - const currentTrackedTokens = this.props.trackedTokens; - const differentTrackedTokens = _.difference(currentTrackedTokens, prevProps.trackedTokens); - const firstDifferentTrackedToken = _.head(differentTrackedTokens); - // check if there is only one different token, and if that token is a member of the current tracked tokens - // this means that the token was added, not removed - if ( - !_.isUndefined(firstDifferentTrackedToken) && - _.size(differentTrackedTokens) === 1 && - _.includes(currentTrackedTokens, firstDifferentTrackedToken) - ) { - document.getElementById(firstDifferentTrackedToken.address).scrollIntoView(); - } - } - public render(): React.ReactNode { - return ( - <Island className="flex flex-column wallet" style={this.props.style}> - {this._isBlockchainReady() ? this._renderLoadedRows() : this._renderLoadingRows()} - </Island> - ); - } - private _renderLoadingRows(): React.ReactNode { - return _.concat(this._renderLoadingHeaderRows(), this._renderLoadingBodyRows()); - } - private _renderLoadingHeaderRows(): React.ReactElement<{}> { - return this._renderPlainHeaderRow('Loading...'); - } - private _renderLoadingBodyRows(): React.ReactElement<{}> { - const bodyStyle = this._getBodyStyle(); - const loadingRowsRange = _.range(LOADING_ROWS_COUNT); - return ( - <div key={BODY_ITEM_KEY} className="flex flex-column" style={bodyStyle}> - {_.map(loadingRowsRange, index => { - return <NullTokenRow key={index} iconDimension={ICON_DIMENSION} fillColor={PLACEHOLDER_COLOR} />; - })} - <Container - className="flex items-center" - position="absolute" - width="100%" - height="100%" - maxHeight={bodyStyle.maxHeight} - > - <div className="mx-auto"> - <BodyOverlay - dispatcher={this.props.dispatcher} - userAddress={this.props.userAddress} - injectedProviderName={this.props.injectedProviderName} - providerType={this.props.providerType} - onToggleLedgerDialog={this.props.onToggleLedgerDialog} - blockchain={this.props.blockchain} - blockchainIsLoaded={this.props.blockchainIsLoaded} - /> - </div> - </Container> - </div> - ); - } - private _renderLoadedRows(): React.ReactNode { - const isAddressAvailable = !_.isEmpty(this.props.userAddress); - return isAddressAvailable - ? _.concat(this._renderConnectedHeaderRows(), this._renderBody()) - : _.concat(this._renderDisconnectedHeaderRows(), this._renderLoadingBodyRows()); - } - private _renderDisconnectedHeaderRows(): React.ReactElement<{}> { - const isExternallyInjectedProvider = utils.isExternallyInjected( - this.props.providerType, - this.props.injectedProviderName, - ); - const text = isExternallyInjectedProvider ? 'Please unlock MetaMask...' : 'Please connect a wallet...'; - return this._renderPlainHeaderRow(text); - } - private _renderPlainHeaderRow(text: string): React.ReactElement<{}> { - return ( - <StandardIconRow - key={HEADER_ITEM_KEY} - icon={<ActionAccountBalanceWallet color={colors.grey} />} - main={ - <Text fontSize="16px" fontColor={colors.grey}> - {text} - </Text> - // https://github.com/palantir/tslint-react/issues/140 - // tslint:disable-next-line:jsx-curly-spacing - } - minHeight="60px" - backgroundColor={colors.white} - /> - ); - } - private _renderConnectedHeaderRows(): React.ReactElement<{}> { - const isMobile = this.props.screenWidth === ScreenWidths.Sm; - const userAddress = this.props.userAddress; - const accountState = this._getAccountState(); - const main = ( - <div className="flex flex-column"> - <Text fontSize="16px" lineHeight="19px" fontWeight={500}> - {utils.getAddressBeginAndEnd(userAddress)} - </Text> - <AccountConnection accountState={accountState} injectedProviderName={this.props.injectedProviderName} /> - </div> - ); - const onClick = _.noop.bind(_); - const accessory = ( - <DropDown - activeNode={ - // this container gives the menu button more of a hover target for the drop down - // it prevents accidentally closing the menu by moving off of the button - <Container paddingLeft="100px" paddingRight="15px"> - <Text - className="zmdi zmdi-more-horiz" - Tag="i" - fontSize="32px" - fontFamily="Material-Design-Iconic-Font" - fontColor={colors.darkGrey} - onClick={onClick} - hoverColor={colors.mediumBlue} - /> - </Container> - } - popoverContent={ - <SimpleMenu minWidth="150px"> - <CopyAddressSimpleMenuItem userAddress={this.props.userAddress} /> - {!isMobile && <DifferentWalletSimpleMenuItem onClick={this.props.onToggleLedgerDialog} />} - <SimpleMenuItem displayText="Add Tokens..." onClick={this.props.onAddToken} /> - <SimpleMenuItem displayText="Remove Tokens..." onClick={this.props.onRemoveToken} /> - {!isMobile && <GoToAccountManagementSimpleMenuItem />} - </SimpleMenu> - } - anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} - targetOrigin={{ horizontal: 'right', vertical: 'top' }} - zDepth={1} - activateEvent={DropdownMouseEvent.Click} - closeEvent={DropdownMouseEvent.Click} - /> - ); - return ( - <StandardIconRow - key={HEADER_ITEM_KEY} - icon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />} - main={main} - accessory={accessory} - minHeight="60px" - backgroundColor={colors.white} - /> - ); - } - private _renderBody(): React.ReactElement<{}> { - const bodyStyle = this._getBodyStyle(); - return ( - <div - style={bodyStyle} - key={BODY_ITEM_KEY} - onMouseEnter={this._onSidebarHover.bind(this)} - onMouseLeave={this._onSidebarHoverOff.bind(this)} - > - {this._renderEthRows()} - {this._renderTokenRows()} - </div> - ); - } - private _getBodyStyle(): React.CSSProperties { - return { - overflow: 'auto', - WebkitOverflowScrolling: 'touch', - position: 'relative', - overflowY: this.state.isHoveringSidebar ? 'scroll' : 'hidden', - marginRight: this.state.isHoveringSidebar ? 0 : 4, - minHeight: '250px', - maxHeight: !utils.isMobileWidth(this.props.screenWidth) ? 'calc(90vh - 300px)' : undefined, - }; - } - private _onSidebarHover(_event: React.FormEvent<HTMLInputElement>): void { - this.setState({ - isHoveringSidebar: true, - }); - } - private _onSidebarHoverOff(): void { - this.setState({ - isHoveringSidebar: false, - }); - } - private _renderEthRows(): React.ReactNode { - const icon = <img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />; - const primaryText = this._renderAmount( - this.props.userEtherBalanceInWei || new BigNumber(0), - constants.DECIMAL_PLACES_ETH, - constants.ETHER_SYMBOL, - _.isUndefined(this.props.userEtherBalanceInWei), - ); - const etherToken = this._getEthToken(); - const etherTokenState = this.props.trackedTokenStateByAddress[etherToken.address]; - const etherPrice = etherTokenState.price; - const secondaryText = this._renderValue( - this.props.userEtherBalanceInWei || new BigNumber(0), - constants.DECIMAL_PLACES_ETH, - etherPrice, - _.isUndefined(this.props.userEtherBalanceInWei) || !etherTokenState.isLoaded, - ); - const accessoryItemConfig = { - wrappedEtherDirection: Side.Deposit, - }; - const key = ETHER_ITEM_KEY; - return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig); - } - private _renderTokenRows(): React.ReactNode { - const trackedTokens = this.props.trackedTokens; - const trackedTokensStartingWithEtherToken = trackedTokens.sort( - firstBy((t: Token) => t.symbol !== constants.ETHER_TOKEN_SYMBOL) - .thenBy((t: Token) => t.symbol !== constants.ZRX_TOKEN_SYMBOL) - .thenBy('trackedTimestamp'), - ); - return _.map(trackedTokensStartingWithEtherToken, this._renderTokenRow.bind(this)); - } - private _renderTokenRow(token: Token): React.ReactNode { - const tokenState = this.props.trackedTokenStateByAddress[token.address]; - if (_.isUndefined(tokenState)) { - return null; - } - const tokenLink = sharedUtils.getEtherScanLinkIfExists( - token.address, - this.props.networkId, - EtherscanLinkSuffixes.Address, - ); - const icon = <TokenIcon token={token} diameter={ICON_DIMENSION} link={tokenLink} />; - const isWeth = token.symbol === constants.ETHER_TOKEN_SYMBOL; - const wrappedEtherDirection = isWeth ? Side.Receive : undefined; - const primaryText = this._renderAmount(tokenState.balance, token.decimals, token.symbol, !tokenState.isLoaded); - const secondaryText = this._renderValue( - tokenState.balance, - token.decimals, - tokenState.price, - !tokenState.isLoaded, - ); - const accessoryItemConfig: AccessoryItemConfig = { - wrappedEtherDirection, - allowanceStateToggleConfig: { - token, - tokenState, - }, - }; - const key = token.address; - return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig); - } - private _renderBalanceRow( - key: string, - icon: React.ReactNode, - primaryText: React.ReactNode, - secondaryText: React.ReactNode, - accessoryItemConfig: AccessoryItemConfig, - className?: string, - ): React.ReactNode { - const shouldShowWrapEtherItem = - !_.isUndefined(this.state.wrappedEtherDirection) && - this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection && - !_.isUndefined(this.props.userEtherBalanceInWei); - const etherToken = this._getEthToken(); - const wrapEtherItem = shouldShowWrapEtherItem ? ( - <WrapEtherItem - userAddress={this.props.userAddress} - networkId={this.props.networkId} - blockchain={this.props.blockchain} - dispatcher={this.props.dispatcher} - userEtherBalanceInWei={this.props.userEtherBalanceInWei} - direction={accessoryItemConfig.wrappedEtherDirection} - etherToken={etherToken} - lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} - onConversionSuccessful={this._closeWrappedEtherActionRow.bind(this)} - // tslint:disable:jsx-no-lambda - refetchEthTokenStateAsync={async () => this.props.refetchTokenStateAsync(etherToken.address)} - /> - ) : null; - return ( - <div id={key} key={key} className={`flex flex-column ${className || ''}`}> - {this.state.wrappedEtherDirection === Side.Receive && wrapEtherItem} - <StandardIconRow - icon={icon} - main={ - <div className="flex flex-column"> - {primaryText} - <Container marginTop="3px">{secondaryText}</Container> - </div> - } - accessory={this._renderAccessoryItems(accessoryItemConfig)} - /> - {this.state.wrappedEtherDirection === Side.Deposit && wrapEtherItem} - </div> - ); - } - private _renderAccessoryItems(config: AccessoryItemConfig): React.ReactElement<{}> { - const shouldShowWrappedEtherAction = !_.isUndefined(config.wrappedEtherDirection); - const shouldShowToggle = !_.isUndefined(config.allowanceStateToggleConfig); - // if we don't have a toggle, we still want some space to the right of the "wrap" button so that it aligns with - // the "unwrap" button in the row below - const isWrapEtherRow = shouldShowWrappedEtherAction && config.wrappedEtherDirection === Side.Deposit; - const width = isWrapEtherRow ? WRAP_ROW_ALLOWANCE_TOGGLE_WIDTH : ALLOWANCE_TOGGLE_WIDTH; - const toggle = ( - <Container className="flex justify-center" width={width}> - {shouldShowToggle && this._renderAllowanceToggle(config.allowanceStateToggleConfig)} - </Container> - ); - return ( - <div className="flex items-center"> - <div className="flex-auto"> - {shouldShowWrappedEtherAction && this._renderWrappedEtherButton(config.wrappedEtherDirection)} - </div> - <div className="flex-last pl2">{toggle}</div> - </div> - ); - } - private _renderAllowanceToggle(config: AllowanceStateToggleConfig): React.ReactNode { - // TODO: Error handling - return ( - <AllowanceStateToggle - blockchain={this.props.blockchain} - token={config.token} - tokenState={config.tokenState} - tooltipDirection={this.props.toggleTooltipDirection} - refetchTokenStateAsync={async () => this.props.refetchTokenStateAsync(config.token.address)} - /> - ); - } - private _renderAmount( - amount: BigNumber, - decimals: number, - symbol: string, - isLoading: boolean = false, - ): React.ReactNode { - if (isLoading) { - return ( - <PlaceHolder hideChildren={isLoading} fillColor={PLACEHOLDER_COLOR}> - <Text fontSize="16px" fontWeight="bold" lineHeight="1em"> - 0.00 XXX - </Text> - </PlaceHolder> - ); - } else { - return <Balance amount={amount} decimals={decimals} symbol={symbol} />; - } - } - private _renderValue( - amount: BigNumber, - decimals: number, - price?: BigNumber, - isLoading: boolean = false, - ): React.ReactNode { - const result = !isLoading - ? _.isUndefined(price) - ? '--' - : utils.getUsdValueFormattedAmount(amount, decimals, price) - : '$0.00'; - return ( - <PlaceHolder hideChildren={isLoading} fillColor={PLACEHOLDER_COLOR}> - <Text fontSize="14px" fontColor={colors.darkGrey} lineHeight="1em"> - {result} - </Text> - </PlaceHolder> - ); - } - private _renderWrappedEtherButton(wrappedEtherDirection: Side): React.ReactNode { - const isWrappedEtherDirectionOpen = this.state.wrappedEtherDirection === wrappedEtherDirection; - let buttonLabel; - let buttonIconName; - if (isWrappedEtherDirectionOpen) { - buttonLabel = 'cancel'; - buttonIconName = 'zmdi-close'; - } else { - switch (wrappedEtherDirection) { - case Side.Deposit: - buttonLabel = 'wrap'; - buttonIconName = 'zmdi-long-arrow-down'; - break; - case Side.Receive: - buttonLabel = 'unwrap'; - buttonIconName = 'zmdi-long-arrow-up'; - break; - default: - throw errorUtils.spawnSwitchErr('wrappedEtherDirection', wrappedEtherDirection); - } - } - const onClick = isWrappedEtherDirectionOpen - ? this._closeWrappedEtherActionRow.bind(this, wrappedEtherDirection) - : this._openWrappedEtherActionRow.bind(this, wrappedEtherDirection); - return ( - <IconButton iconName={buttonIconName} labelText={buttonLabel} onClick={onClick} color={colors.mediumBlue} /> - ); - } - private _openWrappedEtherActionRow(wrappedEtherDirection: Side): void { - const action = - wrappedEtherDirection === Side.Deposit ? 'Wallet - Wrap ETH Opened' : 'Wallet - Unwrap WETH Opened'; - analytics.track(action); - this.setState({ - wrappedEtherDirection, - }); - } - private _closeWrappedEtherActionRow(wrappedEtherDirection: Side): void { - const action = - wrappedEtherDirection === Side.Deposit ? 'Wallet - Wrap ETH Closed' : 'Wallet - Unwrap WETH Closed'; - analytics.track(action); - this.setState({ - wrappedEtherDirection: undefined, - }); - } - private _getEthToken(): Token { - return utils.getEthToken(this.props.tokenByAddress); - } - private _isBlockchainReady(): boolean { - return this.props.blockchainIsLoaded && !_.isUndefined(this.props.blockchain); - } - private _getAccountState(): AccountState { - return utils.getAccountState( - this._isBlockchainReady(), - this.props.providerType, - this.props.injectedProviderName, - this.props.userAddress, - ); - } -} - -// tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx deleted file mode 100644 index 7de3afbf8..000000000 --- a/packages/website/ts/components/wallet/wrap_ether_item.tsx +++ /dev/null @@ -1,230 +0,0 @@ -import { Styles } from '@0x/react-shared'; -import { BigNumber, logUtils } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as _ from 'lodash'; -import FlatButton from 'material-ui/FlatButton'; -import * as React from 'react'; - -import { Blockchain } from 'ts/blockchain'; -import { TokenAmountInput } from 'ts/components/inputs/token_amount_input'; -import { Container } from 'ts/components/ui/container'; -import { EthAmountInput } from 'ts/containers/inputs/eth_amount_input'; -import { Dispatcher } from 'ts/redux/dispatcher'; -import { colors } from 'ts/style/colors'; -import { BlockchainCallErrs, Side, Token } from 'ts/types'; -import { analytics } from 'ts/utils/analytics'; -import { constants } from 'ts/utils/constants'; -import { errorReporter } from 'ts/utils/error_reporter'; -import { utils } from 'ts/utils/utils'; - -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; - isEthConversionHappening: boolean; - errorMsg: React.ReactNode; -} - -const styles: Styles = { - topLabel: { - color: colors.black, - fontSize: 11, - }, - inputContainer: { - backgroundColor: colors.white, - borderBottomRightRadius: 3, - borderBottomLeftRadius: 3, - borderTopRightRadius: 3, - borderTopLeftRadius: 3, - padding: 4, - }, - amountInput: { - height: 34, - }, - amountInputLabel: { - paddingTop: 10, - paddingRight: 10, - paddingLeft: 5, - color: colors.grey, - fontSize: 14, - }, - amountInputHint: { - bottom: 18, - }, - wrapEtherConfirmationButtonLabel: { - fontSize: 12, - color: colors.white, - }, - errorMsg: { - fontSize: 12, - marginTop: 4, - color: colors.red, - minHeight: 20, - }, - conversionSpinner: { - paddingTop: 26, - }, -}; - -export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEtherItemState> { - constructor(props: WrapEtherItemProps) { - super(props); - this.state = { - currentInputAmount: undefined, - isEthConversionHappening: false, - errorMsg: null, - }; - } - public render(): React.ReactNode { - const isWrappingEth = this.props.direction === Side.Deposit; - const topLabelText = isWrappingEth ? 'Convert ETH into WETH 1:1' : 'Convert WETH into ETH 1:1'; - return ( - <Container className="flex" backgroundColor={colors.walletFocusedItemBackground} paddingTop="25px"> - <div>{this._renderIsEthConversionHappeningSpinner()} </div> - <div className="flex flex-column"> - <div style={styles.topLabel}>{topLabelText}</div> - <div className="flex items-center"> - <div style={styles.inputContainer}> - {isWrappingEth ? ( - <EthAmountInput - amount={this.state.currentInputAmount} - hintText="0.00" - onChange={this._onValueChange.bind(this)} - shouldCheckBalance={true} - shouldShowIncompleteErrs={false} - shouldShowErrs={false} - shouldShowUnderline={false} - style={styles.amountInput} - labelStyle={styles.amountInputLabel} - inputHintStyle={styles.amountInputHint} - onErrorMsgChange={this._onErrorMsgChange.bind(this)} - /> - ) : ( - <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" - shouldShowErrs={false} - shouldShowUnderline={false} - style={styles.amountInput} - labelStyle={styles.amountInputLabel} - inputHintStyle={styles.amountInputHint} - onErrorMsgChange={this._onErrorMsgChange.bind(this)} - /> - )} - </div> - <div>{this._renderWrapEtherConfirmationButton()}</div> - </div> - - {this._renderErrorMsg()} - </div> - </Container> - ); - } - private _onValueChange(_isValid: boolean, amount?: BigNumber): void { - this.setState({ - currentInputAmount: amount, - }); - } - private _onErrorMsgChange(errorMsg: React.ReactNode): void { - this.setState({ - errorMsg, - }); - } - private _renderIsEthConversionHappeningSpinner(): React.ReactElement<{}> { - const visibility = this.state.isEthConversionHappening ? 'visible' : 'hidden'; - const style: React.CSSProperties = { ...styles.conversionSpinner, visibility }; - return ( - <div className="pl3 pr2" style={style}> - <i className="zmdi zmdi-spinner zmdi-hc-spin" /> - </div> - ); - } - private _renderWrapEtherConfirmationButton(): React.ReactElement<{}> { - const isWrappingEth = this.props.direction === Side.Deposit; - const labelText = isWrappingEth ? 'wrap' : 'unwrap'; - return ( - <div className="pl1 pr3"> - <FlatButton - backgroundColor={colors.wrapEtherConfirmationButton} - label={labelText} - style={{ zIndex: 0 }} - labelStyle={styles.wrapEtherConfirmationButtonLabel} - onClick={this._wrapEtherConfirmationActionAsync.bind(this)} - disabled={this.state.isEthConversionHappening} - /> - </div> - ); - } - private _renderErrorMsg(): React.ReactNode { - return <div style={styles.errorMsg}>{this.state.errorMsg}</div>; - } - private async _wrapEtherConfirmationActionAsync(): Promise<void> { - this.setState({ - isEthConversionHappening: true, - }); - const etherToken = this.props.etherToken; - const amountToConvert = this.state.currentInputAmount; - const ethAmount = Web3Wrapper.toUnitAmount(amountToConvert, constants.DECIMAL_PLACES_ETH).toString(); - const tokenAmount = Web3Wrapper.toUnitAmount(amountToConvert, etherToken.decimals).toString(); - try { - if (this.props.direction === Side.Deposit) { - await this.props.blockchain.convertEthToWrappedEthTokensAsync(etherToken.address, amountToConvert); - this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount} ETH to WETH`); - analytics.track('Wrap ETH Success', { - amount: ethAmount, - }); - } else { - await this.props.blockchain.convertWrappedEthTokensToEthAsync(etherToken.address, amountToConvert); - this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount} WETH to ETH`); - analytics.track('Unwrap WETH Success', { - amount: tokenAmount, - }); - } - 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); - if (this.props.direction === Side.Deposit) { - this.props.dispatcher.showFlashMessage('Failed to wrap your ETH. Please try again.'); - analytics.track('Wrap ETH Failure', { - amount: ethAmount, - }); - } else { - this.props.dispatcher.showFlashMessage('Failed to unwrap your WETH. Please try again.'); - analytics.track('Unwrap WETH Failed', { - amount: tokenAmount, - }); - } - errorReporter.report(err); - } - } - this.setState({ - isEthConversionHappening: false, - }); - } -} |