aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website/ts/components/wallet/wallet.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/website/ts/components/wallet/wallet.tsx')
-rw-r--r--packages/website/ts/components/wallet/wallet.tsx527
1 files changed, 0 insertions, 527 deletions
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