diff options
Diffstat (limited to 'packages')
24 files changed, 585 insertions, 397 deletions
diff --git a/packages/website/ts/components/inputs/allowance_toggle.tsx b/packages/website/ts/components/inputs/allowance_toggle.tsx index 0dd2a5aa5..0d5995696 100644 --- a/packages/website/ts/components/inputs/allowance_toggle.tsx +++ b/packages/website/ts/components/inputs/allowance_toggle.tsx @@ -48,10 +48,10 @@ const styles: Styles = { width: 25, }, offTrackStyle: { - backgroundColor: colors.allowanceToggleOffTrack, + backgroundColor: colors.grey300, }, onTrackStyle: { - backgroundColor: colors.allowanceToggleOnTrack, + backgroundColor: colors.mediumBlue, }, }; diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index 296b410fe..ccc8d9e86 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -47,8 +47,14 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp public componentWillUnmount(): void { this._unlisten(); } - public componentDidUpdate(): void { + public componentDidUpdate(prevProps: PortalOnboardingFlowProps): void { this._overrideOnboardingStateIfShould(); + if (!prevProps.isRunning && this.props.isRunning) { + // On mobile, make sure the wallet is completely visible. + if (this.props.screenWidth === ScreenWidths.Sm) { + document.querySelector('.wallet').scrollIntoView(); + } + } } public render(): React.ReactNode { return ( diff --git a/packages/website/ts/components/portal/drawer_menu.tsx b/packages/website/ts/components/portal/drawer_menu.tsx index 205a60afc..a6707e86c 100644 --- a/packages/website/ts/components/portal/drawer_menu.tsx +++ b/packages/website/ts/components/portal/drawer_menu.tsx @@ -44,12 +44,13 @@ export const DrawerMenu = (props: DrawerMenuProps) => { iconName: 'zmdi-portable-wifi', }; const menuItemEntries = _.concat(relayerItemEntry, defaultMenuItemEntries); - const displayMessage = utils.getReadableAccountState( + const accountState = utils.getAccountState( props.blockchainIsLoaded && !_.isUndefined(props.blockchain), props.providerType, props.injectedProviderName, props.userAddress, ); + const displayMessage = utils.getReadableAccountState(accountState, props.userAddress); return ( <div style={styles.root}> <Header userAddress={props.userAddress} displayMessage={displayMessage} /> diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 438c7b52f..8a86a7c8d 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -1,10 +1,10 @@ import { colors, constants as sharedConstants } from '@0xproject/react-shared'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; -import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet'; +import Help from 'material-ui/svg-icons/action/help'; import * as React from 'react'; import * as DocumentTitle from 'react-document-title'; -import { Route, RouteComponentProps, Switch } from 'react-router-dom'; +import { Link, Route, RouteComponentProps, Switch } from 'react-router-dom'; import { Blockchain } from 'ts/blockchain'; import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog'; @@ -24,7 +24,6 @@ import { TopBar, TopBarDisplayType } from 'ts/components/top_bar/top_bar'; import { TradeHistory } from 'ts/components/trade_history/trade_history'; import { Container } from 'ts/components/ui/container'; import { FlashMessage } from 'ts/components/ui/flash_message'; -import { Island } from 'ts/components/ui/island'; import { Text } from 'ts/components/ui/text'; import { Wallet } from 'ts/components/wallet/wallet'; import { GenerateOrderForm } from 'ts/containers/generate_order_form'; @@ -319,15 +318,14 @@ export class Portal extends React.Component<PortalProps, PortalState> { ); } private _renderWallet(): React.ReactNode { - const startOnboarding = this._renderStartOnboarding(); const isMobile = utils.isMobile(this.props.screenWidth); // We need room to scroll down for mobile onboarding const marginBottom = isMobile ? '200px' : '15px'; return ( <div> - <Container> - {isMobile && <Container marginBottom="15px">{startOnboarding}</Container>} - <Container marginBottom={marginBottom}> + <Container className="flex flex-column items-center"> + {isMobile && <Container marginBottom="20px">{this._renderStartOnboarding()}</Container>} + <Container marginBottom={marginBottom} width="100%"> <Wallet style={ !isMobile && this.props.isPortalOnboardingShowing @@ -355,7 +353,7 @@ export class Portal extends React.Component<PortalProps, PortalState> { refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this)} /> </Container> - {!isMobile && <Container marginTop="15px">{startOnboarding}</Container>} + {!isMobile && <Container marginTop="8px">{this._renderStartOnboarding()}</Container>} </Container> <PortalOnboardingFlow blockchain={this._blockchain} @@ -366,26 +364,27 @@ export class Portal extends React.Component<PortalProps, PortalState> { ); } private _renderStartOnboarding(): React.ReactNode { - return ( - <Island> - <Container - marginTop="30px" - marginBottom="30px" - marginLeft="30px" - marginRight="30px" - className="flex justify-around items-center" - > - <ActionAccountBalanceWallet style={{ width: '30px', height: '30px' }} color={colors.orange} /> - <Text - fontColor={colors.grey} - fontSize="16px" - center={true} - onClick={this._startOnboarding.bind(this)} - > + const isMobile = utils.isMobile(this.props.screenWidth); + const shouldStartOnboarding = !isMobile || this.props.location.pathname === `${WebsitePaths.Portal}/account`; + const startOnboarding = ( + <Container className="flex items-center center"> + <Help style={{ width: '20px', height: '20px' }} color={colors.mediumBlue} /> + <Container marginLeft="8px"> + <Text fontColor={colors.mediumBlue} fontSize="16px" onClick={this._startOnboarding.bind(this)}> Learn how to set up your account </Text> </Container> - </Island> + </Container> + ); + return !shouldStartOnboarding ? ( + <Link + to={{ pathname: `${WebsitePaths.Portal}/account`, state: { startOnboarding: true } }} + style={{ textDecoration: 'none' }} + > + {startOnboarding} + </Link> + ) : ( + startOnboarding ); } @@ -393,10 +392,6 @@ export class Portal extends React.Component<PortalProps, PortalState> { const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; analytics.logEvent('Portal', 'Onboarding Started - Manual', networkName, this.props.portalOnboardingStep); this.props.dispatcher.updatePortalOnboardingShowing(true); - // On mobile, make sure the wallet is completely visible. - if (this.props.screenWidth === ScreenWidths.Sm) { - document.querySelector('.wallet').scrollIntoView(); - } } private _renderWalletSection(): React.ReactNode { return <Section header={<TextHeader labelText="Your Account" />} body={this._renderWallet()} />; @@ -535,11 +530,15 @@ export class Portal extends React.Component<PortalProps, PortalState> { ); } private _renderRelayerIndexSection(): React.ReactNode { + return <Section header={<TextHeader labelText="0x Relayers" />} body={this._renderRelayerIndex()} />; + } + private _renderRelayerIndex(): React.ReactNode { + const isMobile = utils.isMobile(this.props.screenWidth); return ( - <Section - header={<TextHeader labelText="0x Relayers" />} - body={<RelayerIndex networkId={this.props.networkId} screenWidth={this.props.screenWidth} />} - /> + <Container className="flex flex-column items-center"> + {isMobile && <Container marginBottom="20px">{this._renderStartOnboarding()}</Container>} + <RelayerIndex networkId={this.props.networkId} screenWidth={this.props.screenWidth} /> + </Container> ); } private _renderNotFoundMessage(): React.ReactNode { diff --git a/packages/website/ts/components/portal/section.tsx b/packages/website/ts/components/portal/section.tsx index 455ed07c9..b6c9fd098 100644 --- a/packages/website/ts/components/portal/section.tsx +++ b/packages/website/ts/components/portal/section.tsx @@ -6,9 +6,9 @@ export interface SectionProps { } export const Section = (props: SectionProps) => { return ( - <div className="flex flex-column" style={{ height: '100%' }}> + <div className="flex flex-column"> {props.header} - <div className="flex-auto">{props.body}</div> + {props.body} </div> ); }; diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx index 496e5cae0..8743e4320 100644 --- a/packages/website/ts/components/top_bar/provider_display.tsx +++ b/packages/website/ts/components/top_bar/provider_display.tsx @@ -2,19 +2,21 @@ import { Styles } from '@0xproject/react-shared'; import * as _ from 'lodash'; import CircularProgress from 'material-ui/CircularProgress'; import RaisedButton from 'material-ui/RaisedButton'; +import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet'; +import Lock from 'material-ui/svg-icons/action/lock'; import * as React from 'react'; import { Blockchain } from 'ts/blockchain'; import { ProviderPicker } from 'ts/components/top_bar/provider_picker'; +import { AccountConnection } from 'ts/components/ui/account_connection'; import { Container } from 'ts/components/ui/container'; import { DropDown } from 'ts/components/ui/drop_down'; import { Identicon } from 'ts/components/ui/identicon'; -import { Image } from 'ts/components/ui/image'; import { Island } from 'ts/components/ui/island'; import { Text } from 'ts/components/ui/text'; import { Dispatcher } from 'ts/redux/dispatcher'; import { colors } from 'ts/style/colors'; -import { ProviderType } from 'ts/types'; +import { AccountState, ProviderType } from 'ts/types'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; @@ -46,37 +48,13 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi this.props.providerType, this.props.injectedProviderName, ); - const displayMessage = utils.getReadableAccountState( - this._isBlockchainReady(), - this.props.providerType, - this.props.injectedProviderName, - this.props.userAddress, - ); - // If the "injected" provider is our fallback public node, then we want to - // show the "connect a wallet" message instead of the providerName - const injectedProviderName = isExternallyInjectedProvider - ? this.props.injectedProviderName - : 'Connect a wallet'; - const providerTitle = - this.props.providerType === ProviderType.Injected ? injectedProviderName : 'Ledger Nano S'; - const isProviderMetamask = providerTitle === constants.PROVIDER_NAME_METAMASK; const hoverActiveNode = ( - <Island className="flex items-center p1" style={styles.root}> - <div> - {this._isBlockchainReady() ? ( - <Identicon address={this.props.userAddress} diameter={ROOT_HEIGHT} /> - ) : ( - <CircularProgress size={ROOT_HEIGHT} thickness={2} /> - )} - </div> + <Island className="flex items-center py1 px2" style={styles.root}> + {this._renderIcon()} <Container marginLeft="12px" marginRight="12px"> - <Text fontSize="14px" fontColor={colors.darkGrey}> - {displayMessage} - </Text> + {this._renderDisplayMessage()} </Container> - {isProviderMetamask && ( - <Image src="/images/metamask_icon.png" height={ROOT_HEIGHT} width={ROOT_HEIGHT} /> - )} + {this._renderInjectedProvider()} </Island> ); const hasLedgerProvider = this.props.providerType === ProviderType.Ledger; @@ -168,7 +146,69 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi ); } } + private _renderIcon(): React.ReactNode { + const accountState = this._getAccountState(); + switch (accountState) { + case AccountState.Ready: + return <Identicon address={this.props.userAddress} diameter={ROOT_HEIGHT} />; + case AccountState.Loading: + return <CircularProgress size={ROOT_HEIGHT} thickness={2} />; + case AccountState.Locked: + return <Lock color={colors.black} />; + case AccountState.Disconnected: + return <ActionAccountBalanceWallet color={colors.mediumBlue} />; + default: + return null; + } + } + private _renderDisplayMessage(): React.ReactNode { + const accountState = this._getAccountState(); + const displayMessage = utils.getReadableAccountState(accountState, this.props.userAddress); + const fontColor = this._getDisplayMessageFontColor(); + return ( + <Text fontSize="16px" fontColor={fontColor} fontWeight={500}> + {displayMessage} + </Text> + ); + } + private _getDisplayMessageFontColor(): string { + const accountState = this._getAccountState(); + switch (accountState) { + case AccountState.Loading: + return colors.darkGrey; + case AccountState.Ready: + case AccountState.Locked: + case AccountState.Disconnected: + default: + return colors.black; + } + } + private _renderInjectedProvider(): React.ReactNode { + const accountState = this._getAccountState(); + switch (accountState) { + case AccountState.Ready: + case AccountState.Locked: + return ( + <AccountConnection + accountState={accountState} + injectedProviderName={this.props.injectedProviderName} + /> + ); + case AccountState.Disconnected: + 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, + ); + } } diff --git a/packages/website/ts/components/ui/account_connection.tsx b/packages/website/ts/components/ui/account_connection.tsx new file mode 100644 index 000000000..6d0b90922 --- /dev/null +++ b/packages/website/ts/components/ui/account_connection.tsx @@ -0,0 +1,40 @@ +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 { colors } from 'ts/style/colors'; +import { AccountState } from 'ts/types'; + +export interface AccountConnectionProps { + accountState: AccountState; + injectedProviderName: string; +} + +export const AccountConnection: React.StatelessComponent<AccountConnectionProps> = ({ + accountState, + injectedProviderName, +}) => { + return ( + <Container className="flex items-center"> + <Circle diameter={6} fillColor={getInjectedProviderColor(accountState)} /> + <Container marginLeft="6px"> + <Text fontSize="12px" lineHeight="14px" fontColor={colors.darkGrey}> + {injectedProviderName} + </Text> + </Container> + </Container> + ); +}; + +const getInjectedProviderColor = (accountState: AccountState) => { + switch (accountState) { + case AccountState.Ready: + return colors.limeGreen; + case AccountState.Locked: + case AccountState.Loading: + case AccountState.Disconnected: + default: + return colors.red; + } +}; diff --git a/packages/website/ts/components/ui/circle.tsx b/packages/website/ts/components/ui/circle.tsx new file mode 100644 index 000000000..75103d066 --- /dev/null +++ b/packages/website/ts/components/ui/circle.tsx @@ -0,0 +1,16 @@ +import * as React from 'react'; + +export interface CircleProps { + className?: string; + diameter: number; + fillColor: string; +} + +export const Circle: React.StatelessComponent<CircleProps> = ({ className, diameter, fillColor }) => { + const radius = diameter / 2; + return ( + <svg className={className} height={diameter} width={diameter}> + <circle cx={radius} cy={radius} r={radius} fill={fillColor} /> + </svg> + ); +}; diff --git a/packages/website/ts/components/ui/container.tsx b/packages/website/ts/components/ui/container.tsx index a747ef01f..fb718d731 100644 --- a/packages/website/ts/components/ui/container.tsx +++ b/packages/website/ts/components/ui/container.tsx @@ -14,7 +14,9 @@ export interface ContainerProps { backgroundColor?: string; borderRadius?: StringOrNum; maxWidth?: StringOrNum; + maxHeight?: StringOrNum; width?: StringOrNum; + height?: StringOrNum; minHeight?: StringOrNum; isHidden?: boolean; className?: string; diff --git a/packages/website/ts/components/ui/identicon.tsx b/packages/website/ts/components/ui/identicon.tsx index cc1655962..b5b374973 100644 --- a/packages/website/ts/components/ui/identicon.tsx +++ b/packages/website/ts/components/ui/identicon.tsx @@ -2,6 +2,7 @@ import blockies = require('blockies'); import * as _ from 'lodash'; import * as React from 'react'; +import { Circle } from 'ts/components/ui/circle'; import { Image } from 'ts/components/ui/image'; import { colors } from 'ts/style/colors'; @@ -20,7 +21,6 @@ export class Identicon extends React.Component<IdenticonProps, IdenticonState> { public render(): React.ReactNode { const address = this.props.address; const diameter = this.props.diameter; - const radius = diameter / 2; return ( <div className="circle relative transitionFix" @@ -40,9 +40,7 @@ export class Identicon extends React.Component<IdenticonProps, IdenticonState> { width={diameter} /> ) : ( - <svg height={diameter} width={diameter}> - <circle cx={radius} cy={radius} r={radius} fill={colors.grey200} /> - </svg> + <Circle diameter={diameter} fillColor={colors.grey200} /> )} </div> ); diff --git a/packages/website/ts/components/ui/text.tsx b/packages/website/ts/components/ui/text.tsx index 1e2a123b7..88f216d4e 100644 --- a/packages/website/ts/components/ui/text.tsx +++ b/packages/website/ts/components/ui/text.tsx @@ -15,6 +15,7 @@ export interface TextProps { minHeight?: string; center?: boolean; fontWeight?: number | string; + textDecorationLine?: string; onClick?: () => void; } @@ -28,6 +29,7 @@ export const Text = styled(PlainText)` font-family: ${props => props.fontFamily}; font-weight: ${props => props.fontWeight}; font-size: ${props => props.fontSize}; + text-decoration-line: ${props => props.textDecorationLine}; ${props => (props.lineHeight ? `line-height: ${props.lineHeight}` : '')}; ${props => (props.center ? 'text-align: center' : '')}; color: ${props => props.fontColor}; @@ -45,6 +47,7 @@ Text.defaultProps = { fontColor: colors.black, fontSize: '15px', lineHeight: '1.5em', + textDecorationLine: 'none', Tag: 'div', }; diff --git a/packages/website/ts/components/wallet/body_overlay.tsx b/packages/website/ts/components/wallet/body_overlay.tsx new file mode 100644 index 000000000..5ced704f9 --- /dev/null +++ b/packages/website/ts/components/wallet/body_overlay.tsx @@ -0,0 +1,146 @@ +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, BrowserType, ProviderType } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { utils } from 'ts/utils/utils'; + +const METAMASK_IMG_SRC = '/images/metamask_icon.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"> + <GetMetaMask /> + <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 GetMetaMask = () => { + const browserType = utils.getBrowserType(); + let extensionLink; + switch (browserType) { + case BrowserType.Chrome: + extensionLink = constants.URL_METAMASK_CHROME_STORE; + break; + case BrowserType.Firefox: + extensionLink = constants.URL_METAMASK_FIREFOX_STORE; + break; + case BrowserType.Opera: + extensionLink = constants.URL_METAMASK_OPERA_STORE; + break; + default: + extensionLink = constants.URL_METAMASK_HOMEPAGE; + } + return ( + <a href={extensionLink} target="_blank" style={{ textDecoration: 'none' }}> + <Island + className="flex items-center py1 px2" + style={{ height: 28, borderRadius: 28, backgroundColor: colors.mediumBlue }} + > + <Image src={METAMASK_IMG_SRC} width="28px" /> + <Container marginLeft="8px" marginRight="12px"> + <Text fontColor={colors.white} fontSize="16px" fontWeight={500}> + Get MetaMask Wallet + </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 new file mode 100644 index 000000000..a1ec9871a --- /dev/null +++ b/packages/website/ts/components/wallet/null_token_row.tsx @@ -0,0 +1,41 @@ +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 new file mode 100644 index 000000000..bf40d2ea8 --- /dev/null +++ b/packages/website/ts/components/wallet/placeholder.tsx @@ -0,0 +1,25 @@ +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 new file mode 100644 index 000000000..1a2ec021b --- /dev/null +++ b/packages/website/ts/components/wallet/standard_icon_row.tsx @@ -0,0 +1,44 @@ +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 index 785b2da88..ad6faff8d 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -1,36 +1,31 @@ -import { - constants as sharedConstants, - EtherscanLinkSuffixes, - Styles, - utils as sharedUtils, -} from '@0xproject/react-shared'; +import { constants as sharedConstants, EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared'; import { BigNumber, errorUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; -import CircularProgress from 'material-ui/CircularProgress'; -import FloatingActionButton from 'material-ui/FloatingActionButton'; -import { ListItem } from 'material-ui/List'; import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet'; -import ContentAdd from 'material-ui/svg-icons/content/add'; -import ContentRemove from 'material-ui/svg-icons/content/remove'; import * as React from 'react'; import { Link } from 'react-router-dom'; import firstBy = require('thenby'); import { Blockchain } from 'ts/blockchain'; +import { AccountConnection } from 'ts/components/ui/account_connection'; import { Container } from 'ts/components/ui/container'; import { IconButton } from 'ts/components/ui/icon_button'; import { Identicon } from 'ts/components/ui/identicon'; import { Island } from 'ts/components/ui/island'; +import { Text } from 'ts/components/ui/text'; import { TokenIcon } from 'ts/components/ui/token_icon'; -import { WalletDisconnectedItem } from 'ts/components/wallet/wallet_disconnected_item'; +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 { AllowanceToggle } from 'ts/containers/inputs/allowance_toggle'; import { Dispatcher } from 'ts/redux/dispatcher'; import { colors } from 'ts/style/colors'; -import { styled } from 'ts/style/theme'; import { + AccountState, BlockchainErrs, ProviderType, ScreenWidths, @@ -44,7 +39,6 @@ import { import { analytics } from 'ts/utils/analytics'; 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; @@ -84,66 +78,16 @@ interface AccessoryItemConfig { allowanceToggleConfig?: AllowanceToggleConfig; } -const styles: Styles = { - root: { - width: '100%', - }, - footerItemInnerDiv: { - paddingLeft: 24, - borderTopColor: colors.walletBorder, - borderTopStyle: 'solid', - borderWidth: 1, - }, - borderedItem: { - borderBottomColor: colors.walletBorder, - borderBottomStyle: 'solid', - borderWidth: 1, - }, - tokenItem: { - backgroundColor: colors.walletDefaultItemBackground, - minHeight: 85, - }, - amountLabel: { - fontWeight: 'bold', - color: colors.black, - }, - valueLabel: { - color: colors.grey, - fontSize: 14, - }, - paddedItem: { - paddingTop: 8, - paddingBottom: 8, - }, - bodyInnerDiv: { - overflow: 'auto', - WebkitOverflowScrolling: 'touch', - }, - manageYourWalletText: { - color: colors.mediumBlue, - fontWeight: 'bold', - }, - loadingBody: { - height: 381, - }, -}; - const ETHER_ICON_PATH = '/images/ether.png'; const ICON_DIMENSION = 28; const BODY_ITEM_KEY = 'BODY'; const HEADER_ITEM_KEY = 'HEADER'; -const FOOTER_ITEM_KEY = 'FOOTER'; -const DISCONNECTED_ITEM_KEY = 'DISCONNECTED'; const ETHER_ITEM_KEY = 'ETHER'; const USD_DECIMAL_PLACES = 2; const NO_ALLOWANCE_TOGGLE_SPACE_WIDTH = 56; const ACCOUNT_PATH = `${WebsitePaths.Portal}/account`; - -const ActionButton = styled(FloatingActionButton)` - button { - position: static !important; - } -`; +const PLACEHOLDER_COLOR = colors.grey300; +const LOADING_ROWS_COUNT = 6; export class Wallet extends React.Component<WalletProps, WalletState> { public static defaultProps = { @@ -171,72 +115,103 @@ export class Wallet extends React.Component<WalletProps, WalletState> { } } public render(): React.ReactNode { - const isBlockchainLoaded = this.props.blockchainIsLoaded && this.props.blockchainErr === BlockchainErrs.NoError; return ( - <Island className="flex flex-column wallet" style={{ ...styles.root, ...this.props.style }}> - {isBlockchainLoaded ? this._renderLoadedRows() : this._renderLoadingRows()} + <Island className="flex flex-column wallet" style={this.props.style}> + {this._isBlockchainReady() ? this._renderLoadedRows() : this._renderLoadingRows()} </Island> ); } - private _renderLoadedRows(): React.ReactNode { - const isAddressAvailable = !_.isEmpty(this.props.userAddress); - return isAddressAvailable - ? _.concat(this._renderConnectedHeaderRows(), this._renderBody(), this._renderFooterRows()) - : _.concat(this._renderDisconnectedHeaderRows(), this._renderDisconnectedRows()); - } private _renderLoadingRows(): React.ReactNode { - return _.concat(this._renderDisconnectedHeaderRows(), this._renderLoadingBodyRows()); + 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 items-center" style={styles.loadingBody}> - <div className="mx-auto"> - <CircularProgress size={40} thickness={5} /> - </div> + <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 primaryText = 'wallet'; - return ( - <StandardIconRow - key={HEADER_ITEM_KEY} - icon={<ActionAccountBalanceWallet color={colors.mediumBlue} />} - main={primaryText.toUpperCase()} - style={styles.borderedItem} - /> + 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 _renderDisconnectedRows(): React.ReactElement<{}> { + private _renderPlainHeaderRow(text: string): React.ReactElement<{}> { return ( - <WalletDisconnectedItem - key={DISCONNECTED_ITEM_KEY} - providerType={this.props.providerType} - injectedProviderName={this.props.injectedProviderName} - onToggleLedgerDialog={this.props.onToggleLedgerDialog} + <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 userAddress = this.props.userAddress; - const primaryText = utils.getAddressBeginAndEnd(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> + ); return ( <Link key={HEADER_ITEM_KEY} to={ACCOUNT_PATH} style={{ textDecoration: 'none' }}> <StandardIconRow icon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />} - main={primaryText} - style={styles.borderedItem} + main={main} + minHeight="60px" + backgroundColor={colors.white} /> </Link> ); } private _renderBody(): React.ReactElement<{}> { - const bodyStyle: React.CSSProperties = { - ...styles.bodyInnerDiv, - overflow: this.state.isHoveringSidebar ? 'auto' : 'hidden', - // TODO: make this completely responsive - maxHeight: this.props.screenWidth !== ScreenWidths.Sm ? 475 : undefined, - }; + const bodyStyle = this._getBodyStyle(); return ( <div style={bodyStyle} @@ -249,6 +224,17 @@ export class Wallet extends React.Component<WalletProps, WalletState> { </div> ); } + private _getBodyStyle(): React.CSSProperties { + return { + overflow: 'auto', + WebkitOverflowScrolling: 'touch', + position: 'relative', + overflowY: this.state.isHoveringSidebar ? 'scroll' : 'hidden', + marginRight: this.state.isHoveringSidebar ? 0 : 4, + // TODO: make this completely responsive + maxHeight: this.props.screenWidth !== ScreenWidths.Sm ? 475 : undefined, + }; + } private _onSidebarHover(_event: React.FormEvent<HTMLInputElement>): void { this.setState({ isHoveringSidebar: true, @@ -259,51 +245,6 @@ export class Wallet extends React.Component<WalletProps, WalletState> { isHoveringSidebar: false, }); } - private _renderFooterRows(): React.ReactElement<{}> { - return ( - <div key={FOOTER_ITEM_KEY}> - <ListItem - primaryText={ - <div className="flex"> - <ActionButton mini={true} zDepth={0} onClick={this.props.onAddToken}> - <ContentAdd /> - </ActionButton> - <ActionButton mini={true} zDepth={0} className="px1" onClick={this.props.onRemoveToken}> - <ContentRemove /> - </ActionButton> - <div - style={{ - paddingLeft: 10, - position: 'relative', - top: '50%', - transform: 'translateY(33%)', - }} - > - add/remove tokens - </div> - </div> - } - disabled={true} - innerDivStyle={styles.footerItemInnerDiv} - style={styles.borderedItem} - /> - {this.props.location.pathname !== ACCOUNT_PATH && ( - <Link to={ACCOUNT_PATH} style={{ textDecoration: 'none' }}> - <ListItem - primaryText={ - <div className="flex right" style={styles.manageYourWalletText}> - manage your wallet - </div> - // https://github.com/palantir/tslint-react/issues/140 - // tslint:disable-next-line:jsx-curly-spacing - } - style={{ ...styles.paddedItem, ...styles.borderedItem }} - /> - </Link> - )} - </div> - ); - } private _renderEthRows(): React.ReactNode { const icon = <img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />; const primaryText = this._renderAmount( @@ -325,7 +266,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { wrappedEtherDirection: Side.Deposit, }; const key = ETHER_ITEM_KEY; - return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig, false, 'eth-row'); + return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig, 'eth-row'); } private _renderTokenRows(): React.ReactNode { const trackedTokens = this.props.trackedTokens; @@ -336,7 +277,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { ); return _.map(trackedTokensStartingWithEtherToken, this._renderTokenRow.bind(this)); } - private _renderTokenRow(token: Token, index: number): React.ReactNode { + private _renderTokenRow(token: Token): React.ReactNode { const tokenState = this.props.trackedTokenStateByAddress[token.address]; if (_.isUndefined(tokenState)) { return null; @@ -364,14 +305,12 @@ export class Wallet extends React.Component<WalletProps, WalletState> { }, }; const key = token.address; - const isLastRow = index === this.props.trackedTokens.length - 1; return this._renderBalanceRow( key, icon, primaryText, secondaryText, accessoryItemConfig, - isLastRow, isWeth ? 'weth-row' : undefined, ); } @@ -381,20 +320,12 @@ export class Wallet extends React.Component<WalletProps, WalletState> { primaryText: React.ReactNode, secondaryText: React.ReactNode, accessoryItemConfig: AccessoryItemConfig, - isLastRow: boolean, className?: string, ): React.ReactNode { const shouldShowWrapEtherItem = !_.isUndefined(this.state.wrappedEtherDirection) && this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection && !_.isUndefined(this.props.userEtherBalanceInWei); - let additionalStyle; - if (shouldShowWrapEtherItem) { - additionalStyle = walletItemStyles.focusedItem; - } else if (!isLastRow) { - additionalStyle = styles.borderedItem; - } - const style = { ...styles.tokenItem, ...additionalStyle }; const etherToken = this._getEthToken(); return ( <div id={key} key={key} className={`flex flex-column ${className || ''}`}> @@ -407,7 +338,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { </div> } accessory={this._renderAccessoryItems(accessoryItemConfig)} - style={style} + backgroundColor={shouldShowWrapEtherItem ? colors.walletFocusedItemBackground : undefined} /> {shouldShowWrapEtherItem && ( <WrapEtherItem @@ -466,13 +397,19 @@ export class Wallet extends React.Component<WalletProps, WalletState> { ): React.ReactNode { if (isLoading) { return ( - <PlaceHolder hideChildren={isLoading}> - <div style={styles.amountLabel}>0.00 XXX</div> + <PlaceHolder hideChildren={isLoading} fillColor={PLACEHOLDER_COLOR}> + <Text fontSize="16px" fontWeight="bold" lineHeight="1em"> + 0.00 XXX + </Text> </PlaceHolder> ); } else { const result = utils.getFormattedAmount(amount, decimals, symbol); - return <div style={styles.amountLabel}>{result}</div>; + return ( + <Text fontSize="16px" fontWeight="bold" lineHeight="1em"> + {result} + </Text> + ); } } private _renderValue( @@ -495,8 +432,10 @@ export class Wallet extends React.Component<WalletProps, WalletState> { result = '$0.00'; } return ( - <PlaceHolder hideChildren={isLoading}> - <div style={styles.valueLabel}>{result}</div> + <PlaceHolder hideChildren={isLoading} fillColor={PLACEHOLDER_COLOR}> + <Text fontSize="14px" fontColor={colors.darkGrey} lineHeight="1em"> + {result} + </Text> </PlaceHolder> ); } @@ -549,41 +488,17 @@ export class Wallet extends React.Component<WalletProps, WalletState> { 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, + ); + } } -interface StandardIconRowProps { - icon: React.ReactNode; - main: React.ReactNode; - accessory?: React.ReactNode; - style?: React.CSSProperties; -} -const StandardIconRow = (props: StandardIconRowProps) => { - return ( - <div className="flex items-center" style={props.style}> - <div className="p2">{props.icon}</div> - <div className="flex-none pr2 pt2 pb2">{props.main}</div> - <div className="flex-auto" /> - <div>{props.accessory}</div> - </div> - ); -}; -interface PlaceHolderProps { - hideChildren: React.ReactNode; - children?: React.ReactNode; -} -const PlaceHolder = (props: PlaceHolderProps) => { - const rootBackgroundColor = props.hideChildren ? colors.lightGrey : 'transparent'; - const rootStyle: React.CSSProperties = { - backgroundColor: rootBackgroundColor, - display: 'inline-block', - borderRadius: 2, - }; - const childrenVisibility = props.hideChildren ? 'hidden' : 'visible'; - const childrenStyle: React.CSSProperties = { visibility: childrenVisibility }; - return ( - <div style={rootStyle}> - <div style={childrenStyle}>{props.children}</div> - </div> - ); -}; // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/wallet/wallet_disconnected_item.tsx b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx deleted file mode 100644 index 024b28544..000000000 --- a/packages/website/ts/components/wallet/wallet_disconnected_item.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { Styles } from '@0xproject/react-shared'; -import FlatButton from 'material-ui/FlatButton'; -import * as React from 'react'; - -import { colors } from 'ts/style/colors'; -import { BrowserType, ProviderType } from 'ts/types'; -import { constants } from 'ts/utils/constants'; -import { utils } from 'ts/utils/utils'; - -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, // 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', - }, -}; - -const ITEM_HEIGHT = 381; -const METAMASK_ICON_WIDTH = 35; -const LEDGER_ICON_WIDTH = 30; -const BUTTON_BOTTOM_PADDING = 80; - -export const WalletDisconnectedItem: React.StatelessComponent<WalletDisconnectedItemProps> = ( - props: WalletDisconnectedItemProps, -) => { - const isExternallyInjectedProvider = utils.isExternallyInjected(props.providerType, props.injectedProviderName); - return ( - <div className="flex flex-center"> - <div className="mx-auto"> - <div className="table" style={{ height: ITEM_HEIGHT }}> - <div className="table-cell align-middle"> - <ProviderButton isExternallyInjectedProvider={isExternallyInjectedProvider} /> - <div className="flex flex-center py2" style={{ paddingBottom: BUTTON_BOTTOM_PADDING }}> - <div className="mx-auto"> - <div onClick={props.onToggleLedgerDialog} style={{ cursor: 'pointer' }}> - <img src="/images/ledger_icon.png" style={{ width: LEDGER_ICON_WIDTH }} /> - <span className="px1" style={styles.otherWalletText}> - user other wallet - </span> - </div> - </div> - </div> - </div> - </div> - </div> - </div> - ); -}; - -interface ProviderButtonProps { - isExternallyInjectedProvider: boolean; -} - -const ProviderButton: React.StatelessComponent<ProviderButtonProps> = (props: ProviderButtonProps) => { - const browserType = utils.getBrowserType(); - let extensionLink; - if (!props.isExternallyInjectedProvider) { - switch (browserType) { - case BrowserType.Chrome: - extensionLink = constants.URL_METAMASK_CHROME_STORE; - break; - case BrowserType.Firefox: - extensionLink = constants.URL_METAMASK_FIREFOX_STORE; - break; - case BrowserType.Opera: - extensionLink = constants.URL_METAMASK_OPERA_STORE; - break; - default: - extensionLink = constants.URL_METAMASK_HOMEPAGE; - } - } - return ( - <FlatButton - label={props.isExternallyInjectedProvider ? 'Please unlock account' : 'Get Metamask Wallet Extension'} - labelStyle={{ color: colors.black }} - labelPosition="after" - primary={true} - icon={<img src="/images/metamask_icon.png" width={METAMASK_ICON_WIDTH.toString()} />} - style={props.isExternallyInjectedProvider ? styles.button : { ...styles.button, ...styles.hrefAdjustment }} - href={extensionLink} - target={props.isExternallyInjectedProvider ? undefined : '_blank'} - disabled={props.isExternallyInjectedProvider} - /> - ); -}; diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx index d6135ce4d..f6d1a9a35 100644 --- a/packages/website/ts/components/wallet/wrap_ether_item.tsx +++ b/packages/website/ts/components/wallet/wrap_ether_item.tsx @@ -15,7 +15,6 @@ 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'; -import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles'; export interface WrapEtherItemProps { userAddress: string; @@ -95,7 +94,7 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther const topLabelText = isWrappingEth ? 'Convert ETH into WETH 1:1' : 'Convert WETH into ETH 1:1'; return ( - <div className="flex" style={walletItemStyles.focusedItem}> + <div className="flex" style={{ backgroundColor: colors.walletFocusedItemBackground }}> <div>{this._renderIsEthConversionHappeningSpinner()} </div> <div className="flex flex-column"> <div style={styles.topLabel}>{topLabelText}</div> diff --git a/packages/website/ts/pages/jobs/list/list_item.tsx b/packages/website/ts/pages/jobs/list/list_item.tsx index d7838bc01..192433d39 100644 --- a/packages/website/ts/pages/jobs/list/list_item.tsx +++ b/packages/website/ts/pages/jobs/list/list_item.tsx @@ -1,14 +1,14 @@ import * as React from 'react'; +import { Circle } from 'ts/components/ui/circle'; + export interface ListItemProps { bulletColor?: string; } export const ListItem: React.StatelessComponent<ListItemProps> = ({ bulletColor, children }) => { return ( <div className="flex items-center"> - <svg className="flex-none lg-px2 md-px2 sm-pl2" height="26" width="26"> - <circle cx="13" cy="13" r="13" fill={bulletColor || 'transparent'} /> - </svg> + <Circle className="flex-none lg-px2 md-px2 sm-pl2" diameter={26} fillColor={bulletColor || 'transparent'} /> <div className="flex-auto px2">{children}</div> </div> ); diff --git a/packages/website/ts/style/colors.ts b/packages/website/ts/style/colors.ts index 45be4fe7f..349845a09 100644 --- a/packages/website/ts/style/colors.ts +++ b/packages/website/ts/style/colors.ts @@ -6,13 +6,13 @@ const appColors = { walletDefaultItemBackground: '#fbfbfc', walletFocusedItemBackground: '#f0f1f4', allowanceToggleShadow: 'rgba(0, 0, 0, 0)', - allowanceToggleOffTrack: '#adadad', - allowanceToggleOnTrack: sharedColors.mediumBlue, wrapEtherConfirmationButton: sharedColors.mediumBlue, drawerMenuBackground: '#4a4a4a', menuItemDefaultSelectedBackground: '#424242', jobsPageBackground: sharedColors.grey50, jobsPageOpenPositionRow: sharedColors.grey100, + metaMaskOrange: '#f68c24', + metaMaskTransparentOrange: 'rgba(255, 248, 242, 0.8)', }; export const colors = { diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 3211ddbd0..498a0a5b8 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -564,4 +564,11 @@ export enum BrowserType { Opera = 'Opera', Other = 'Other', } + +export enum AccountState { + Disconnected = 'Disconnected', + Ready = 'Ready', + Loading = 'Loading', + Locked = 'Locked', +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index a3f8eacb0..d3a2aa107 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -26,7 +26,7 @@ export const constants = { NETWORK_ID_TESTRPC: 50, NULL_ADDRESS: '0x0000000000000000000000000000000000000000', PROVIDER_NAME_LEDGER: 'Ledger', - PROVIDER_NAME_METAMASK: 'Metamask', + PROVIDER_NAME_METAMASK: 'MetaMask', PROVIDER_NAME_PARITY_SIGNER: 'Parity Signer', PROVIDER_NAME_MIST: 'Mist', PROVIDER_NAME_GENERIC: 'Injected Web3', diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index d12ae6a98..726e1815f 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -9,6 +9,7 @@ import deepEqual = require('deep-equal'); import * as _ from 'lodash'; import * as moment from 'moment'; import { + AccountState, BlockchainCallErrs, BrowserType, Environments, @@ -192,23 +193,37 @@ export const utils = { const truncatedAddress = `${address.substring(0, 6)}...${address.substr(-4)}`; // 0x3d5a...b287 return truncatedAddress; }, - getReadableAccountState( + getReadableAccountState(accountState: AccountState, userAddress: string): string { + switch (accountState) { + case AccountState.Loading: + return 'Loading...'; + case AccountState.Ready: + return utils.getAddressBeginAndEnd(userAddress); + case AccountState.Locked: + return 'Please Unlock'; + case AccountState.Disconnected: + return 'Connect a Wallet'; + default: + return ''; + } + }, + getAccountState( isBlockchainReady: boolean, providerType: ProviderType, injectedProviderName: string, userAddress?: string, - ): string { + ): AccountState { const isAddressAvailable = !_.isUndefined(userAddress) && !_.isEmpty(userAddress); const isExternallyInjectedProvider = utils.isExternallyInjected(providerType, injectedProviderName); if (!isBlockchainReady) { - return 'Loading account'; + return AccountState.Loading; } else if (isAddressAvailable) { - return utils.getAddressBeginAndEnd(userAddress); + return AccountState.Ready; // tslint:disable-next-line: prefer-conditional-expression } else if (isExternallyInjectedProvider) { - return 'Account locked'; + return AccountState.Locked; } else { - return 'No wallet detected'; + return AccountState.Disconnected; } }, hasUniqueNameAndSymbol(tokens: Token[], token: Token): boolean { diff --git a/packages/website/ts/utils/wallet_item_styles.ts b/packages/website/ts/utils/wallet_item_styles.ts deleted file mode 100644 index 9d6033d74..000000000 --- a/packages/website/ts/utils/wallet_item_styles.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Styles } from '@0xproject/react-shared'; - -import { colors } from 'ts/style/colors'; - -export const styles: Styles = { - focusedItem: { - backgroundColor: colors.walletFocusedItemBackground, - }, -}; |