From 11f1ccf3ff92d5d1e32e0451ffd5069615e29867 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 23 Apr 2018 21:07:48 -0700 Subject: Lay out wallet and relayers --- packages/website/ts/components/portal/portal.tsx | 214 +++++++++++++++++++++++ packages/website/ts/containers/portal.ts | 88 ++++++++++ packages/website/ts/index.tsx | 11 +- 3 files changed, 310 insertions(+), 3 deletions(-) create mode 100644 packages/website/ts/components/portal/portal.tsx create mode 100644 packages/website/ts/containers/portal.ts (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx new file mode 100644 index 000000000..8a9e89a72 --- /dev/null +++ b/packages/website/ts/components/portal/portal.tsx @@ -0,0 +1,214 @@ +import { colors, Styles } from '@0xproject/react-shared'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; +import * as DocumentTitle from 'react-document-title'; + +import { Blockchain } from 'ts/blockchain'; +import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog'; +import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_dialog'; +import { PortalDisclaimerDialog } from 'ts/components/dialogs/portal_disclaimer_dialog'; +import { RelayerIndex } from 'ts/components/relayer_index/relayer_index'; +import { TopBar } from 'ts/components/top_bar/top_bar'; +import { FlashMessage } from 'ts/components/ui/flash_message'; +import { Wallet } from 'ts/components/wallet/wallet'; +import { localStorage } from 'ts/local_storage/local_storage'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, TokenByAddress } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { Translate } from 'ts/utils/translate'; +import { utils } from 'ts/utils/utils'; + +export interface PortalProps { + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + dispatcher: Dispatcher; + hashData: HashData; + injectedProviderName: string; + networkId: number; + nodeVersion: string; + orderFillAmount: BigNumber; + providerType: ProviderType; + screenWidth: ScreenWidths; + tokenByAddress: TokenByAddress; + userEtherBalanceInWei: BigNumber; + userAddress: string; + shouldBlockchainErrDialogBeOpen: boolean; + userSuppliedOrderCache: Order; + location: Location; + flashMessage?: string | React.ReactNode; + lastForceTokenStateRefetch: number; + translate: Translate; +} + +interface PortalState { + prevNetworkId: number; + prevNodeVersion: string; + prevUserAddress: string; + prevPathname: string; + isDisclaimerDialogOpen: boolean; + isLedgerDialogOpen: boolean; +} + +const THROTTLE_TIMEOUT = 100; +const TOP_BAR_HEIGHT = 60; + +const styles: Styles = { + root: { + width: '100%', + height: '100%', + backgroundColor: colors.lightestGrey, + }, + body: { + height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, + }, +}; + +export class Portal extends React.Component { + private _blockchain: Blockchain; + private _throttledScreenWidthUpdate: () => void; + constructor(props: PortalProps) { + super(props); + this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); + const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER); + const hasAcceptedDisclaimer = + !_.isUndefined(didAcceptPortalDisclaimer) && !_.isEmpty(didAcceptPortalDisclaimer); + this.state = { + prevNetworkId: this.props.networkId, + prevNodeVersion: this.props.nodeVersion, + prevUserAddress: this.props.userAddress, + prevPathname: this.props.location.pathname, + isDisclaimerDialogOpen: !hasAcceptedDisclaimer, + isLedgerDialogOpen: false, + }; + } + public componentDidMount() { + window.addEventListener('resize', this._throttledScreenWidthUpdate); + window.scrollTo(0, 0); + } + public componentWillMount() { + this._blockchain = new Blockchain(this.props.dispatcher); + } + public componentWillUnmount() { + this._blockchain.destroy(); + window.removeEventListener('resize', this._throttledScreenWidthUpdate); + // We re-set the entire redux state when the portal is unmounted so that when it is re-rendered + // the initialization process always occurs from the same base state. This helps avoid + // initialization inconsistencies (i.e While the portal was unrendered, the user might have + // become disconnected from their backing Ethereum node, changes user accounts, etc...) + this.props.dispatcher.resetState(); + } + public componentWillReceiveProps(nextProps: PortalProps) { + if (nextProps.networkId !== this.state.prevNetworkId) { + // tslint:disable-next-line:no-floating-promises + this._blockchain.networkIdUpdatedFireAndForgetAsync(nextProps.networkId); + this.setState({ + prevNetworkId: nextProps.networkId, + }); + } + if (nextProps.userAddress !== this.state.prevUserAddress) { + const newUserAddress = _.isEmpty(nextProps.userAddress) ? undefined : nextProps.userAddress; + // tslint:disable-next-line:no-floating-promises + this._blockchain.userAddressUpdatedFireAndForgetAsync(newUserAddress); + this.setState({ + prevUserAddress: nextProps.userAddress, + }); + } + if (nextProps.nodeVersion !== this.state.prevNodeVersion) { + // tslint:disable-next-line:no-floating-promises + this._blockchain.nodeVersionUpdatedFireAndForgetAsync(nextProps.nodeVersion); + } + if (nextProps.location.pathname !== this.state.prevPathname) { + this.setState({ + prevPathname: nextProps.location.pathname, + }); + } + } + public render() { + const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind( + this.props.dispatcher, + ); + const allTokens = _.values(this.props.tokenByAddress); + const trackedTokens = _.filter(allTokens, t => t.isTracked); + return ( +
+ + +
+
+
+ +
+
+ +
+
+ + + + {this.props.blockchainIsLoaded && ( + + )} +
+
+ ); + } + private _onToggleLedgerDialog() { + this.setState({ + isLedgerDialogOpen: !this.state.isLedgerDialogOpen, + }); + } + private _onPortalDisclaimerAccepted() { + localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set'); + this.setState({ + isDisclaimerDialogOpen: false, + }); + } + private _updateScreenWidth() { + const newScreenWidth = utils.getScreenWidth(); + this.props.dispatcher.updateScreenWidth(newScreenWidth); + } +} diff --git a/packages/website/ts/containers/portal.ts b/packages/website/ts/containers/portal.ts new file mode 100644 index 000000000..3f0feb6e9 --- /dev/null +++ b/packages/website/ts/containers/portal.ts @@ -0,0 +1,88 @@ +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; +import { Portal as PortalComponent, PortalProps as PortalComponentProps } from 'ts/components/portal/portal'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { State } from 'ts/redux/reducer'; +import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, Side, TokenByAddress } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { Translate } from 'ts/utils/translate'; + +interface ConnectedState { + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + hashData: HashData; + injectedProviderName: string; + networkId: number; + nodeVersion: string; + orderFillAmount: BigNumber; + providerType: ProviderType; + tokenByAddress: TokenByAddress; + lastForceTokenStateRefetch: number; + userEtherBalanceInWei: BigNumber; + screenWidth: ScreenWidths; + shouldBlockchainErrDialogBeOpen: boolean; + userAddress: string; + userSuppliedOrderCache: Order; + flashMessage?: string | React.ReactNode; + translate: Translate; +} + +interface ConnectedDispatch { + dispatcher: Dispatcher; +} + +const mapStateToProps = (state: State, ownProps: PortalComponentProps): ConnectedState => { + const receiveAssetToken = state.sideToAssetToken[Side.Receive]; + const depositAssetToken = state.sideToAssetToken[Side.Deposit]; + const receiveAddress = !_.isUndefined(receiveAssetToken.address) + ? receiveAssetToken.address + : constants.NULL_ADDRESS; + const depositAddress = !_.isUndefined(depositAssetToken.address) + ? depositAssetToken.address + : constants.NULL_ADDRESS; + const receiveAmount = !_.isUndefined(receiveAssetToken.amount) ? receiveAssetToken.amount : new BigNumber(0); + const depositAmount = !_.isUndefined(depositAssetToken.amount) ? depositAssetToken.amount : new BigNumber(0); + const hashData = { + depositAmount, + depositTokenContractAddr: depositAddress, + feeRecipientAddress: constants.NULL_ADDRESS, + makerFee: constants.MAKER_FEE, + orderExpiryTimestamp: state.orderExpiryTimestamp, + orderMakerAddress: state.userAddress, + orderTakerAddress: state.orderTakerAddress !== '' ? state.orderTakerAddress : constants.NULL_ADDRESS, + receiveAmount, + receiveTokenContractAddr: receiveAddress, + takerFee: constants.TAKER_FEE, + orderSalt: state.orderSalt, + }; + return { + blockchainErr: state.blockchainErr, + blockchainIsLoaded: state.blockchainIsLoaded, + hashData, + injectedProviderName: state.injectedProviderName, + networkId: state.networkId, + nodeVersion: state.nodeVersion, + orderFillAmount: state.orderFillAmount, + providerType: state.providerType, + screenWidth: state.screenWidth, + shouldBlockchainErrDialogBeOpen: state.shouldBlockchainErrDialogBeOpen, + tokenByAddress: state.tokenByAddress, + lastForceTokenStateRefetch: state.lastForceTokenStateRefetch, + userAddress: state.userAddress, + userEtherBalanceInWei: state.userEtherBalanceInWei, + userSuppliedOrderCache: state.userSuppliedOrderCache, + flashMessage: state.flashMessage, + translate: state.translate, + }; +}; + +const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ + dispatcher: new Dispatcher(dispatch), +}); + +export const Portal: React.ComponentClass = connect(mapStateToProps, mapDispatchToProps)( + PortalComponent, +); diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index d99187151..7b7a7eb2e 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -34,9 +34,14 @@ import 'less/all.less'; // cause we only want to import the module when the user navigates to the page. // At the same time webpack statically parses for System.import() to determine bundle chunk split points // so each lazy import needs it's own `System.import()` declaration. -const LazyPortal = createLazyComponent('LegacyPortal', async () => - System.import(/* webpackChunkName: "legacyPortal" */ 'ts/containers/legacy_portal'), -); +const LazyPortal = + utils.isDevelopment() || utils.isStaging() + ? createLazyComponent('Portal', async () => + System.import(/* webpackChunkName: "portal" */ 'ts/containers/portal'), + ) + : createLazyComponent('LegacyPortal', async () => + System.import(/* webpackChunkName: "legacyPortal" */ 'ts/containers/legacy_portal'), + ); const LazyZeroExJSDocumentation = createLazyComponent('Documentation', async () => System.import(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'), ); -- cgit From deaa5f3211d75e81dd7e0820686a5f6a9b26f9d4 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 23 Apr 2018 21:45:05 -0700 Subject: Add scrolling to relayer index --- packages/website/ts/components/portal/portal.tsx | 8 +++++++- packages/website/ts/components/relayer_index/relayer_index.tsx | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 8a9e89a72..6939d8905 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -62,6 +62,12 @@ const styles: Styles = { body: { height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, }, + scrollContainer: { + overflowZ: 'hidden', + height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, + WebkitOverflowScrolling: 'touch', + overflow: 'auto', + }, }; export class Portal extends React.Component { @@ -165,7 +171,7 @@ export class Portal extends React.Component { onToggleLedgerDialog={this._onToggleLedgerDialog.bind(this)} /> -
+
diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index 50760c32d..ba0db7c47 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -57,7 +57,7 @@ export class RelayerIndex extends React.Component +
Date: Mon, 23 Apr 2018 22:17:26 -0700 Subject: Add headers to wallet and relayer index --- packages/website/ts/components/portal/portal.tsx | 12 +++++++++++- .../website/ts/components/relayer_index/relayer_index.tsx | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 6939d8905..507860ee6 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -68,6 +68,10 @@ const styles: Styles = { WebkitOverflowScrolling: 'touch', overflow: 'auto', }, + title: { + fontWeight: 'bold', + fontSize: 20, + }, }; export class Portal extends React.Component { @@ -154,7 +158,10 @@ export class Portal extends React.Component { />
-
+
+
+ Your Account +
{ />
+
+ Explore 0x Ecosystem +
diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index ba0db7c47..50760c32d 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -57,7 +57,7 @@ export class RelayerIndex extends React.Component +
Date: Mon, 23 Apr 2018 23:47:04 -0700 Subject: Relayer grid spacing tweaks --- packages/website/ts/components/relayer_index/relayer_index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index 50760c32d..1e92dba85 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -32,9 +32,9 @@ const styles: Styles = { }, }; -const CELL_HEIGHT = 260; +const CELL_HEIGHT = 290; const NUMBER_OF_COLUMNS = 4; -const GRID_PADDING = 16; +const GRID_PADDING = 20; export class RelayerIndex extends React.Component { private _isUnmounted: boolean; -- cgit From 9c827a0298df3e54e8b18e9f38b1d4027d2b5d83 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 23 Apr 2018 23:47:37 -0700 Subject: Top bar expanded display type --- packages/website/ts/components/portal/portal.tsx | 5 ++- packages/website/ts/components/top_bar/top_bar.tsx | 45 ++++++++++++++-------- 2 files changed, 33 insertions(+), 17 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 507860ee6..bf12c924f 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -9,7 +9,7 @@ import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_dialog'; import { PortalDisclaimerDialog } from 'ts/components/dialogs/portal_disclaimer_dialog'; import { RelayerIndex } from 'ts/components/relayer_index/relayer_index'; -import { TopBar } from 'ts/components/top_bar/top_bar'; +import { TopBar, TopBarDisplayType } from 'ts/components/top_bar/top_bar'; import { FlashMessage } from 'ts/components/ui/flash_message'; import { Wallet } from 'ts/components/wallet/wallet'; import { localStorage } from 'ts/local_storage/local_storage'; @@ -51,7 +51,7 @@ interface PortalState { } const THROTTLE_TIMEOUT = 100; -const TOP_BAR_HEIGHT = 60; +const TOP_BAR_HEIGHT = TopBar.heightForDisplayType(TopBarDisplayType.Expanded); const styles: Styles = { root: { @@ -154,6 +154,7 @@ export class Portal extends React.Component { location={this.props.location} blockchain={this._blockchain} translate={this.props.translate} + displayType={TopBarDisplayType.Expanded} style={{ backgroundColor: colors.lightestGrey }} />
diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index 0c32f4c62..eecec92fb 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -19,7 +19,12 @@ import { Deco, Key, ProviderType, WebsiteLegacyPaths, WebsitePaths } from 'ts/ty import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; -interface TopBarProps { +export enum TopBarDisplayType { + Default, + Expanded, +} + +export interface TopBarProps { userAddress?: string; networkId?: number; injectedProviderName?: string; @@ -34,7 +39,7 @@ interface TopBarProps { availableDocVersions?: string[]; menu?: DocsMenu; menuSubsectionsBySection?: MenuSubsectionsBySection; - shouldFullWidth?: boolean; + displayType?: TopBarDisplayType; docsInfo?: DocsInfo; style?: React.CSSProperties; isNightVersion?: boolean; @@ -56,8 +61,7 @@ const styles: Styles = { width: 70, }, topBar: { - backgroundcolor: colors.white, - height: 59, + backgroundColor: colors.white, width: '100%', position: 'relative', top: 0, @@ -78,12 +82,19 @@ const styles: Styles = { }, }; +const DEFAULT_HEIGHT = 59; +const EXPANDED_HEIGHT = 75; + export class TopBar extends React.Component { public static defaultProps: Partial = { - shouldFullWidth: false, + displayType: TopBarDisplayType.Default, style: {}, isNightVersion: false, }; + public static heightForDisplayType(displayType: TopBarDisplayType) { + const result = displayType === TopBarDisplayType.Expanded ? EXPANDED_HEIGHT : DEFAULT_HEIGHT; + return result + 1; + } constructor(props: TopBarProps) { super(props); this.state = { @@ -92,8 +103,9 @@ export class TopBar extends React.Component { } public render() { const isNightVersion = this.props.isNightVersion; - const isFullWidthPage = this.props.shouldFullWidth; - const parentClassNames = `flex mx-auto ${isFullWidthPage ? 'pl2' : 'max-width-4'}`; + const isExpandedDisplayType = this.props.displayType === TopBarDisplayType.Expanded; + const parentClassNames = `flex mx-auto ${isExpandedDisplayType ? 'pl3 py1' : 'max-width-4'}`; + const height = isExpandedDisplayType ? EXPANDED_HEIGHT : DEFAULT_HEIGHT; const developerSectionMenuItems = [ @@ -172,9 +184,11 @@ export class TopBar extends React.Component { , ]; const bottomBorderStyle = this._shouldDisplayBottomBar() ? styles.bottomBar : {}; - const fullWidthClasses = isFullWidthPage ? 'pr4' : ''; + const fullWidthClasses = isExpandedDisplayType ? 'pr4' : ''; const logoUrl = isNightVersion ? '/images/protocol_logo_white.png' : '/images/protocol_logo_black.png'; - const menuClasses = `col col-${isFullWidthPage ? '4' : '5'} ${fullWidthClasses} lg-pr0 md-pr2 sm-hide xs-hide`; + const menuClasses = `col col-${ + isExpandedDisplayType ? '4' : '5' + } ${fullWidthClasses} lg-pr0 md-pr2 sm-hide xs-hide`; const menuIconStyle = { fontSize: 25, color: isNightVersion ? 'white' : 'black', @@ -191,15 +205,15 @@ export class TopBar extends React.Component { ); const popoverContent = {developerSectionMenuItems}; return ( -
+
-
-
+
+
{!this._isViewingPortal() && (
@@ -236,7 +250,7 @@ export class TopBar extends React.Component { path={`${WebsitePaths.Portal}`} isPrimary={true} style={styles.menuItem} - className={`${isFullWidthPage && 'md-hide'}`} + className={`${isExpandedDisplayType && 'md-hide'}`} isNightVersion={isNightVersion} isExternal={false} /> @@ -256,7 +270,7 @@ export class TopBar extends React.Component { />
)} -
+
@@ -505,7 +519,8 @@ export class TopBar extends React.Component { this._isViewingJsonSchemasDocs() || this._isViewingSolCovDocs() || this._isViewingSubprovidersDocs() || - this._isViewingConnectDocs() + this._isViewingConnectDocs() || + this._isViewingPortal() ); } } // tslint:disable:max-file-line-count -- cgit From b8c611de2b82657a274c55007ffc5d72807eae7f Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 24 Apr 2018 01:04:57 -0700 Subject: ProviderDisplay changes --- .../ts/components/top_bar/provider_display.tsx | 40 +++++++++++++++------- packages/website/ts/components/top_bar/top_bar.tsx | 25 +------------- 2 files changed, 28 insertions(+), 37 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx index 221c34f8c..fa5609014 100644 --- a/packages/website/ts/components/top_bar/provider_display.tsx +++ b/packages/website/ts/components/top_bar/provider_display.tsx @@ -1,4 +1,4 @@ -import { colors } from '@0xproject/react-shared'; +import { colors, Styles } from '@0xproject/react-shared'; import * as _ from 'lodash'; import RaisedButton from 'material-ui/RaisedButton'; import * as React from 'react'; @@ -11,9 +11,9 @@ import { ProviderType } from 'ts/types'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; -const IDENTICON_DIAMETER = 32; +const IDENTICON_DIAMETER = 24; -interface ProviderDisplayProps { +export interface ProviderDisplayProps { dispatcher: Dispatcher; userAddress: string; networkId: number; @@ -25,6 +25,18 @@ interface ProviderDisplayProps { interface ProviderDisplayState {} +const styles: Styles = { + root: { + height: 24, + backgroundColor: colors.white, + borderBottomRightRadius: 24, + borderBottomLeftRadius: 24, + borderTopRightRadius: 24, + borderTopLeftRadius: 24, + boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, + }, +}; + export class ProviderDisplay extends React.Component { public render() { const isAddressAvailable = !_.isEmpty(this.props.userAddress); @@ -40,21 +52,23 @@ export class ProviderDisplay extends React.Component +
-
-
{providerTitle}
-
{displayAddress}
-
-
- +
+
{displayAddress}
+ {isProviderMetamask && ( +
+ +
+ )}
); const hasInjectedProvider = diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index eecec92fb..8c842d2a5 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -52,14 +52,6 @@ interface TopBarState { } const styles: Styles = { - address: { - marginRight: 12, - overflow: 'hidden', - paddingTop: 4, - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - width: 70, - }, topBar: { backgroundColor: colors.white, width: '100%', @@ -258,7 +250,7 @@ export class TopBar extends React.Component {
)} {this.props.blockchainIsLoaded && ( -
+
{
); } - private _renderUser() { - const userAddress = this.props.userAddress; - const identiconDiameter = 26; - return ( -
-
- {!_.isEmpty(userAddress) ? userAddress : ''} -
- {userAddress} -
- -
-
- ); - } private _onMenuButtonClick() { this.setState({ isDrawerOpen: !this.state.isDrawerOpen, -- cgit From 58794fc8e9ac473154ead395452b8e43c10f5cd7 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 23 Apr 2018 21:07:48 -0700 Subject: Lay out wallet and relayers --- packages/website/ts/components/portal/portal.tsx | 214 +++++++++++++++++++++++ packages/website/ts/containers/portal.ts | 88 ++++++++++ packages/website/ts/index.tsx | 11 +- 3 files changed, 310 insertions(+), 3 deletions(-) create mode 100644 packages/website/ts/components/portal/portal.tsx create mode 100644 packages/website/ts/containers/portal.ts (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx new file mode 100644 index 000000000..8a9e89a72 --- /dev/null +++ b/packages/website/ts/components/portal/portal.tsx @@ -0,0 +1,214 @@ +import { colors, Styles } from '@0xproject/react-shared'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; +import * as DocumentTitle from 'react-document-title'; + +import { Blockchain } from 'ts/blockchain'; +import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog'; +import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_dialog'; +import { PortalDisclaimerDialog } from 'ts/components/dialogs/portal_disclaimer_dialog'; +import { RelayerIndex } from 'ts/components/relayer_index/relayer_index'; +import { TopBar } from 'ts/components/top_bar/top_bar'; +import { FlashMessage } from 'ts/components/ui/flash_message'; +import { Wallet } from 'ts/components/wallet/wallet'; +import { localStorage } from 'ts/local_storage/local_storage'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, TokenByAddress } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { Translate } from 'ts/utils/translate'; +import { utils } from 'ts/utils/utils'; + +export interface PortalProps { + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + dispatcher: Dispatcher; + hashData: HashData; + injectedProviderName: string; + networkId: number; + nodeVersion: string; + orderFillAmount: BigNumber; + providerType: ProviderType; + screenWidth: ScreenWidths; + tokenByAddress: TokenByAddress; + userEtherBalanceInWei: BigNumber; + userAddress: string; + shouldBlockchainErrDialogBeOpen: boolean; + userSuppliedOrderCache: Order; + location: Location; + flashMessage?: string | React.ReactNode; + lastForceTokenStateRefetch: number; + translate: Translate; +} + +interface PortalState { + prevNetworkId: number; + prevNodeVersion: string; + prevUserAddress: string; + prevPathname: string; + isDisclaimerDialogOpen: boolean; + isLedgerDialogOpen: boolean; +} + +const THROTTLE_TIMEOUT = 100; +const TOP_BAR_HEIGHT = 60; + +const styles: Styles = { + root: { + width: '100%', + height: '100%', + backgroundColor: colors.lightestGrey, + }, + body: { + height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, + }, +}; + +export class Portal extends React.Component { + private _blockchain: Blockchain; + private _throttledScreenWidthUpdate: () => void; + constructor(props: PortalProps) { + super(props); + this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); + const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER); + const hasAcceptedDisclaimer = + !_.isUndefined(didAcceptPortalDisclaimer) && !_.isEmpty(didAcceptPortalDisclaimer); + this.state = { + prevNetworkId: this.props.networkId, + prevNodeVersion: this.props.nodeVersion, + prevUserAddress: this.props.userAddress, + prevPathname: this.props.location.pathname, + isDisclaimerDialogOpen: !hasAcceptedDisclaimer, + isLedgerDialogOpen: false, + }; + } + public componentDidMount() { + window.addEventListener('resize', this._throttledScreenWidthUpdate); + window.scrollTo(0, 0); + } + public componentWillMount() { + this._blockchain = new Blockchain(this.props.dispatcher); + } + public componentWillUnmount() { + this._blockchain.destroy(); + window.removeEventListener('resize', this._throttledScreenWidthUpdate); + // We re-set the entire redux state when the portal is unmounted so that when it is re-rendered + // the initialization process always occurs from the same base state. This helps avoid + // initialization inconsistencies (i.e While the portal was unrendered, the user might have + // become disconnected from their backing Ethereum node, changes user accounts, etc...) + this.props.dispatcher.resetState(); + } + public componentWillReceiveProps(nextProps: PortalProps) { + if (nextProps.networkId !== this.state.prevNetworkId) { + // tslint:disable-next-line:no-floating-promises + this._blockchain.networkIdUpdatedFireAndForgetAsync(nextProps.networkId); + this.setState({ + prevNetworkId: nextProps.networkId, + }); + } + if (nextProps.userAddress !== this.state.prevUserAddress) { + const newUserAddress = _.isEmpty(nextProps.userAddress) ? undefined : nextProps.userAddress; + // tslint:disable-next-line:no-floating-promises + this._blockchain.userAddressUpdatedFireAndForgetAsync(newUserAddress); + this.setState({ + prevUserAddress: nextProps.userAddress, + }); + } + if (nextProps.nodeVersion !== this.state.prevNodeVersion) { + // tslint:disable-next-line:no-floating-promises + this._blockchain.nodeVersionUpdatedFireAndForgetAsync(nextProps.nodeVersion); + } + if (nextProps.location.pathname !== this.state.prevPathname) { + this.setState({ + prevPathname: nextProps.location.pathname, + }); + } + } + public render() { + const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind( + this.props.dispatcher, + ); + const allTokens = _.values(this.props.tokenByAddress); + const trackedTokens = _.filter(allTokens, t => t.isTracked); + return ( +
+ + +
+
+
+ +
+
+ +
+
+ + + + {this.props.blockchainIsLoaded && ( + + )} +
+
+ ); + } + private _onToggleLedgerDialog() { + this.setState({ + isLedgerDialogOpen: !this.state.isLedgerDialogOpen, + }); + } + private _onPortalDisclaimerAccepted() { + localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set'); + this.setState({ + isDisclaimerDialogOpen: false, + }); + } + private _updateScreenWidth() { + const newScreenWidth = utils.getScreenWidth(); + this.props.dispatcher.updateScreenWidth(newScreenWidth); + } +} diff --git a/packages/website/ts/containers/portal.ts b/packages/website/ts/containers/portal.ts new file mode 100644 index 000000000..3f0feb6e9 --- /dev/null +++ b/packages/website/ts/containers/portal.ts @@ -0,0 +1,88 @@ +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; +import { Portal as PortalComponent, PortalProps as PortalComponentProps } from 'ts/components/portal/portal'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { State } from 'ts/redux/reducer'; +import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, Side, TokenByAddress } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { Translate } from 'ts/utils/translate'; + +interface ConnectedState { + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + hashData: HashData; + injectedProviderName: string; + networkId: number; + nodeVersion: string; + orderFillAmount: BigNumber; + providerType: ProviderType; + tokenByAddress: TokenByAddress; + lastForceTokenStateRefetch: number; + userEtherBalanceInWei: BigNumber; + screenWidth: ScreenWidths; + shouldBlockchainErrDialogBeOpen: boolean; + userAddress: string; + userSuppliedOrderCache: Order; + flashMessage?: string | React.ReactNode; + translate: Translate; +} + +interface ConnectedDispatch { + dispatcher: Dispatcher; +} + +const mapStateToProps = (state: State, ownProps: PortalComponentProps): ConnectedState => { + const receiveAssetToken = state.sideToAssetToken[Side.Receive]; + const depositAssetToken = state.sideToAssetToken[Side.Deposit]; + const receiveAddress = !_.isUndefined(receiveAssetToken.address) + ? receiveAssetToken.address + : constants.NULL_ADDRESS; + const depositAddress = !_.isUndefined(depositAssetToken.address) + ? depositAssetToken.address + : constants.NULL_ADDRESS; + const receiveAmount = !_.isUndefined(receiveAssetToken.amount) ? receiveAssetToken.amount : new BigNumber(0); + const depositAmount = !_.isUndefined(depositAssetToken.amount) ? depositAssetToken.amount : new BigNumber(0); + const hashData = { + depositAmount, + depositTokenContractAddr: depositAddress, + feeRecipientAddress: constants.NULL_ADDRESS, + makerFee: constants.MAKER_FEE, + orderExpiryTimestamp: state.orderExpiryTimestamp, + orderMakerAddress: state.userAddress, + orderTakerAddress: state.orderTakerAddress !== '' ? state.orderTakerAddress : constants.NULL_ADDRESS, + receiveAmount, + receiveTokenContractAddr: receiveAddress, + takerFee: constants.TAKER_FEE, + orderSalt: state.orderSalt, + }; + return { + blockchainErr: state.blockchainErr, + blockchainIsLoaded: state.blockchainIsLoaded, + hashData, + injectedProviderName: state.injectedProviderName, + networkId: state.networkId, + nodeVersion: state.nodeVersion, + orderFillAmount: state.orderFillAmount, + providerType: state.providerType, + screenWidth: state.screenWidth, + shouldBlockchainErrDialogBeOpen: state.shouldBlockchainErrDialogBeOpen, + tokenByAddress: state.tokenByAddress, + lastForceTokenStateRefetch: state.lastForceTokenStateRefetch, + userAddress: state.userAddress, + userEtherBalanceInWei: state.userEtherBalanceInWei, + userSuppliedOrderCache: state.userSuppliedOrderCache, + flashMessage: state.flashMessage, + translate: state.translate, + }; +}; + +const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ + dispatcher: new Dispatcher(dispatch), +}); + +export const Portal: React.ComponentClass = connect(mapStateToProps, mapDispatchToProps)( + PortalComponent, +); diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index 9535dd222..1b1255214 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -34,9 +34,14 @@ import 'less/all.less'; // cause we only want to import the module when the user navigates to the page. // At the same time webpack statically parses for System.import() to determine bundle chunk split points // so each lazy import needs it's own `System.import()` declaration. -const LazyPortal = createLazyComponent('LegacyPortal', async () => - System.import(/* webpackChunkName: "legacyPortal" */ 'ts/containers/legacy_portal'), -); +const LazyPortal = + utils.isDevelopment() || utils.isStaging() + ? createLazyComponent('Portal', async () => + System.import(/* webpackChunkName: "portal" */ 'ts/containers/portal'), + ) + : createLazyComponent('LegacyPortal', async () => + System.import(/* webpackChunkName: "legacyPortal" */ 'ts/containers/legacy_portal'), + ); const LazyZeroExJSDocumentation = createLazyComponent('Documentation', async () => System.import(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'), ); -- cgit From 14b29172b1713610c80ccf5b7390f7b445b0eb75 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 23 Apr 2018 21:45:05 -0700 Subject: Add scrolling to relayer index --- packages/website/ts/components/portal/portal.tsx | 8 +++++++- packages/website/ts/components/relayer_index/relayer_index.tsx | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 8a9e89a72..6939d8905 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -62,6 +62,12 @@ const styles: Styles = { body: { height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, }, + scrollContainer: { + overflowZ: 'hidden', + height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, + WebkitOverflowScrolling: 'touch', + overflow: 'auto', + }, }; export class Portal extends React.Component { @@ -165,7 +171,7 @@ export class Portal extends React.Component { onToggleLedgerDialog={this._onToggleLedgerDialog.bind(this)} />
-
+
diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index c1ab4227a..e8775e1a5 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -57,7 +57,7 @@ export class RelayerIndex extends React.Component +
Date: Mon, 23 Apr 2018 22:17:26 -0700 Subject: Add headers to wallet and relayer index --- packages/website/ts/components/portal/portal.tsx | 12 +++++++++++- .../website/ts/components/relayer_index/relayer_index.tsx | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 6939d8905..507860ee6 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -68,6 +68,10 @@ const styles: Styles = { WebkitOverflowScrolling: 'touch', overflow: 'auto', }, + title: { + fontWeight: 'bold', + fontSize: 20, + }, }; export class Portal extends React.Component { @@ -154,7 +158,10 @@ export class Portal extends React.Component { />
-
+
+
+ Your Account +
{ />
+
+ Explore 0x Ecosystem +
diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index e8775e1a5..c1ab4227a 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -57,7 +57,7 @@ export class RelayerIndex extends React.Component +
Date: Wed, 25 Apr 2018 11:16:07 -0700 Subject: Add token flow --- .../ts/components/legacy_portal/legacy_portal.tsx | 47 ---------------------- .../legacy_portal/legacy_portal_menu.tsx | 20 --------- packages/website/ts/components/portal/portal.tsx | 35 +++++++++++++++- packages/website/ts/components/wallet/wallet.tsx | 10 ++++- 4 files changed, 43 insertions(+), 69 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/legacy_portal/legacy_portal.tsx b/packages/website/ts/components/legacy_portal/legacy_portal.tsx index 8942e4356..c45b20365 100644 --- a/packages/website/ts/components/legacy_portal/legacy_portal.tsx +++ b/packages/website/ts/components/legacy_portal/legacy_portal.tsx @@ -154,7 +154,6 @@ export class LegacyPortal extends React.Component {this.props.blockchainIsLoaded ? ( - {isDevelopment && ( - - )} - {isDevelopment && ( - - )} t.isTracked); - return ( -
-
- -
-
- ); - } - private _renderRelayers() { - return ( -
-
- -
-
- ); - } private _renderEthWrapper() { return ( {this._renderMenuItemWithIcon('Wrap ETH', 'zmdi-circle-o')} - {configs.ENVIRONMENT === Environments.DEVELOPMENT && ( -
- - {this._renderMenuItemWithIcon('Wallet', 'zmdi-balance-wallet')} - - - {this._renderMenuItemWithIcon('Relayers', 'zmdi-input-antenna')} - -
- )}
); } diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 507860ee6..bee2cf95d 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -8,13 +8,14 @@ import { Blockchain } from 'ts/blockchain'; import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog'; import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_dialog'; import { PortalDisclaimerDialog } from 'ts/components/dialogs/portal_disclaimer_dialog'; +import { AssetPicker } from 'ts/components/generate_order/asset_picker'; import { RelayerIndex } from 'ts/components/relayer_index/relayer_index'; import { TopBar } from 'ts/components/top_bar/top_bar'; import { FlashMessage } from 'ts/components/ui/flash_message'; import { Wallet } from 'ts/components/wallet/wallet'; import { localStorage } from 'ts/local_storage/local_storage'; import { Dispatcher } from 'ts/redux/dispatcher'; -import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, TokenByAddress } from 'ts/types'; +import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, TokenByAddress, TokenVisibility } from 'ts/types'; import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; import { utils } from 'ts/utils/utils'; @@ -48,6 +49,7 @@ interface PortalState { prevPathname: string; isDisclaimerDialogOpen: boolean; isLedgerDialogOpen: boolean; + isAssetPickerDialogOpen: boolean; } const THROTTLE_TIMEOUT = 100; @@ -89,6 +91,7 @@ export class Portal extends React.Component { prevUserAddress: this.props.userAddress, prevPathname: this.props.location.pathname, isDisclaimerDialogOpen: !hasAcceptedDisclaimer, + isAssetPickerDialogOpen: false, isLedgerDialogOpen: false, }; } @@ -176,6 +179,7 @@ export class Portal extends React.Component { injectedProviderName={this.props.injectedProviderName} providerType={this.props.providerType} onToggleLedgerDialog={this._onToggleLedgerDialog.bind(this)} + onAddToken={this._onAddToken.bind(this)} />
@@ -208,15 +212,44 @@ export class Portal extends React.Component { isOpen={this.state.isLedgerDialogOpen} /> )} +
); } + private _onTokenChosen(tokenAddress: string) { + if (_.isEmpty(tokenAddress)) { + this.setState({ + isAssetPickerDialogOpen: false, + }); + return; + } + const token = this.props.tokenByAddress[tokenAddress]; + this.props.dispatcher.updateTokenByAddress([token]); + this.setState({ + isAssetPickerDialogOpen: false, + }); + } private _onToggleLedgerDialog() { this.setState({ isLedgerDialogOpen: !this.state.isLedgerDialogOpen, }); } + private _onAddToken() { + this.setState({ + isAssetPickerDialogOpen: !this.state.isAssetPickerDialogOpen, + }); + } private _onPortalDisclaimerAccepted() { localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set'); this.setState({ diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 057c712e5..068764027 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -55,6 +55,7 @@ export interface WalletProps { injectedProviderName: string; providerType: ProviderType; onToggleLedgerDialog: () => void; + onAddToken: () => void; } interface WalletState { @@ -232,7 +233,14 @@ export class Wallet extends React.Component { } private _renderFooterRows() { const primaryText = '+ other tokens'; - return ; + return ( + + ); } private _renderEthRows() { const primaryText = this._renderAmount( -- cgit From 419b670aa35eb35a767137121b5e594a04793eb7 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 3 May 2018 15:15:13 -0700 Subject: Make wallet scrollable --- packages/website/ts/components/wallet/wallet.tsx | 57 ++++++++++++++++++++---- 1 file changed, 48 insertions(+), 9 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 068764027..a28012aaf 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -61,6 +61,7 @@ export interface WalletProps { interface WalletState { trackedTokenStateByAddress: TokenStateByAddress; wrappedEtherDirection?: Side; + isHoveringSidebar: boolean; } interface AllowanceToggleConfig { @@ -95,6 +96,9 @@ const styles: Styles = { }, footerItemInnerDiv: { paddingLeft: 24, + borderTopColor: colors.walletBorder, + borderTopStyle: 'solid', + borderWidth: 1, }, borderedItem: { borderBottomColor: colors.walletBorder, @@ -115,7 +119,17 @@ const styles: Styles = { paddingTop: 8, paddingBottom: 8, }, - accessoryItemsContainer: { width: 150, right: 8 }, + accessoryItemsContainer: { + width: 150, + right: 8, + }, + bodyInnerDiv: { + padding: 0, + // TODO: make this completely responsive + maxHeight: 475, + overflow: 'auto', + WebkitOverflowScrolling: 'touch', + }, }; const ETHER_ICON_PATH = '/images/ether.png'; @@ -140,6 +154,7 @@ export class Wallet extends React.Component { this.state = { trackedTokenStateByAddress: initialTrackedTokenStateByAddress, wrappedEtherDirection: undefined, + isHoveringSidebar: false, }; } public componentWillMount() { @@ -185,12 +200,7 @@ export class Wallet extends React.Component { return ( {isAddressAvailable - ? _.concat( - this._renderConnectedHeaderRows(), - this._renderEthRows(), - this._renderTokenRows(), - this._renderFooterRows(), - ) + ? _.concat(this._renderConnectedHeaderRows(), this._renderBody(), this._renderFooterRows()) : _.concat(this._renderDisconnectedHeaderRows(), this._renderDisconnectedRows())} ); @@ -231,6 +241,33 @@ export class Wallet extends React.Component { /> ); } + private _renderBody() { + const bodyStyle: React.CSSProperties = { + ...styles.bodyInnerDiv, + overflow: this.state.isHoveringSidebar ? 'auto' : 'hidden', + }; + return ( + + {this._renderEthRows()} + {this._renderTokenRows()} + + ); + } + private _onSidebarHover(event: React.FormEvent) { + this.setState({ + isHoveringSidebar: true, + }); + } + private _onSidebarHoverOff() { + this.setState({ + isHoveringSidebar: false, + }); + } private _renderFooterRows() { const primaryText = '+ other tokens'; return ( @@ -301,7 +338,7 @@ export class Wallet extends React.Component { ); return _.map(trackedTokensStartingWithEtherToken, this._renderTokenRow.bind(this)); } - private _renderTokenRow(token: Token) { + private _renderTokenRow(token: Token, index: number) { const tokenState = this.state.trackedTokenStateByAddress[token.address]; const tokenLink = sharedUtils.getEtherScanLinkIfExists( token.address, @@ -318,12 +355,14 @@ export class Wallet extends React.Component { tokenState, }, }; + // if this is the last item in the list, do not render the border, it is rendered by the footer + const borderedStyle = index !== this.props.trackedTokens.length - 1 ? styles.borderedItem : {}; const shouldShowWrapEtherItem = !_.isUndefined(this.state.wrappedEtherDirection) && this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection; const style = shouldShowWrapEtherItem ? { ...walletItemStyles.focusedItem, ...styles.paddedItem } - : { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem }; + : { ...styles.tokenItem, ...borderedStyle, ...styles.paddedItem }; const etherToken = this._getEthToken(); return (
-- cgit From 9cbd1516586fd5514cce8d216881bad3871c58fa Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 3 May 2018 15:26:30 -0700 Subject: Only show untracked tokens --- packages/website/ts/components/portal/portal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index bee2cf95d..f6077edf8 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -221,7 +221,7 @@ export class Portal extends React.Component { currentTokenAddress={''} onTokenChosen={this._onTokenChosen.bind(this)} tokenByAddress={this.props.tokenByAddress} - tokenVisibility={TokenVisibility.ALL} + tokenVisibility={TokenVisibility.UNTRACKED} />
-- cgit From 6c38481550d3bc57413ffb013d1b94a53672202b Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 8 May 2018 10:17:01 -0700 Subject: Fix typo --- packages/website/ts/components/portal/portal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index f6077edf8..e198a9f03 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -108,7 +108,7 @@ export class Portal extends React.Component { // We re-set the entire redux state when the portal is unmounted so that when it is re-rendered // the initialization process always occurs from the same base state. This helps avoid // initialization inconsistencies (i.e While the portal was unrendered, the user might have - // become disconnected from their backing Ethereum node, changes user accounts, etc...) + // become disconnected from their backing Ethereum node, changed user accounts, etc...) this.props.dispatcher.resetState(); } public componentWillReceiveProps(nextProps: PortalProps) { -- cgit From 17f3d5f6bac178ffda6060eea227ef8deaaf73aa Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 8 May 2018 11:12:46 -0700 Subject: Suggestions and fix bad merge --- packages/website/ts/components/portal/portal.tsx | 9 --------- .../website/ts/components/top_bar/provider_display.tsx | 18 ++++++------------ 2 files changed, 6 insertions(+), 21 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 88003a94e..d359219cd 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -9,11 +9,7 @@ import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_dialog'; import { PortalDisclaimerDialog } from 'ts/components/dialogs/portal_disclaimer_dialog'; import { RelayerIndex } from 'ts/components/relayer_index/relayer_index'; -<<<<<<< HEAD import { TopBar, TopBarDisplayType } from 'ts/components/top_bar/top_bar'; -======= -import { TopBar } from 'ts/components/top_bar/top_bar'; ->>>>>>> development import { FlashMessage } from 'ts/components/ui/flash_message'; import { Wallet } from 'ts/components/wallet/wallet'; import { localStorage } from 'ts/local_storage/local_storage'; @@ -55,11 +51,7 @@ interface PortalState { } const THROTTLE_TIMEOUT = 100; -<<<<<<< HEAD const TOP_BAR_HEIGHT = TopBar.heightForDisplayType(TopBarDisplayType.Expanded); -======= -const TOP_BAR_HEIGHT = 60; ->>>>>>> development const styles: Styles = { root: { @@ -71,7 +63,6 @@ const styles: Styles = { height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, }, scrollContainer: { - overflowZ: 'hidden', height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, WebkitOverflowScrolling: 'touch', overflow: 'auto', diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx index fa5609014..4cd50dba9 100644 --- a/packages/website/ts/components/top_bar/provider_display.tsx +++ b/packages/website/ts/components/top_bar/provider_display.tsx @@ -11,7 +11,7 @@ import { ProviderType } from 'ts/types'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; -const IDENTICON_DIAMETER = 24; +const ROOT_HEIGHT = 24; export interface ProviderDisplayProps { dispatcher: Dispatcher; @@ -27,12 +27,9 @@ interface ProviderDisplayState {} const styles: Styles = { root: { - height: 24, + height: ROOT_HEIGHT, backgroundColor: colors.white, - borderBottomRightRadius: 24, - borderBottomLeftRadius: 24, - borderTopRightRadius: 24, - borderTopLeftRadius: 24, + borderRadius: ROOT_HEIGHT, boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, }, }; @@ -52,21 +49,18 @@ export class ProviderDisplay extends React.Component
- +
{displayAddress}
{isProviderMetamask && (
- +
)}
-- cgit From 5ed9b8b1dc042319fd9ddbdb886c93a660a96769 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 8 May 2018 11:16:12 -0700 Subject: Remove overflowZ property from portal --- packages/website/ts/components/portal/portal.tsx | 1 - 1 file changed, 1 deletion(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index e198a9f03..1038d0dc2 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -65,7 +65,6 @@ const styles: Styles = { height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, }, scrollContainer: { - overflowZ: 'hidden', height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, WebkitOverflowScrolling: 'touch', overflow: 'auto', -- cgit From f94d4b492dc0a2bfdd8bfa8d9a1376ed33b03060 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 8 May 2018 11:32:44 -0700 Subject: Display top tokens from backend --- .../components/relayer_index/relayer_grid_tile.tsx | 33 +--------------------- .../relayer_index/relayer_top_tokens.tsx | 16 +++++------ packages/website/ts/types.ts | 9 +++++- 3 files changed, 17 insertions(+), 41 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index 0b9b8165e..677c66695 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -12,37 +12,6 @@ export interface RelayerGridTileProps { networkId: number; } -// TODO: Get top tokens from remote -const topTokens = [ - { - address: '0x1dad4783cf3fe3085c1426157ab175a6119a04ba', - decimals: 18, - iconUrl: '/images/token_icons/makerdao.png', - isRegistered: true, - isTracked: true, - name: 'Maker DAO', - symbol: 'MKR', - }, - { - address: '0x323b5d4c32345ced77393b3530b1eed0f346429d', - decimals: 18, - iconUrl: '/images/token_icons/melon.png', - isRegistered: true, - isTracked: true, - name: 'Melon Token', - symbol: 'MLN', - }, - { - address: '0xb18845c260f680d5b9d84649638813e342e4f8c9', - decimals: 18, - iconUrl: '/images/token_icons/augur.png', - isRegistered: true, - isTracked: true, - name: 'Augur Reputation Token', - symbol: 'REP', - }, -]; - const styles: Styles = { root: { backgroundColor: colors.white, @@ -108,7 +77,7 @@ export const RelayerGridTile: React.StatelessComponent = (
Daily Trade Volume
- +
Top tokens
diff --git a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx index 233590b78..db4d3a211 100644 --- a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx +++ b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx @@ -3,10 +3,10 @@ import * as _ from 'lodash'; import * as React from 'react'; import { TokenIcon } from 'ts/components/ui/token_icon'; -import { Token } from 'ts/types'; +import { WebsiteBackendTokenInfo } from 'ts/types'; export interface TopTokensProps { - tokens: Token[]; + tokens: WebsiteBackendTokenInfo[]; networkId: number; } @@ -23,17 +23,17 @@ const styles: Styles = { export const TopTokens: React.StatelessComponent = (props: TopTokensProps) => { return (
- {_.map(props.tokens, (token: Token, index: number) => { + {_.map(props.tokens, (tokenInfo: WebsiteBackendTokenInfo, index: number) => { const firstItemStyle = { ...styles.tokenLabel, ...styles.followingTokenLabel }; const style = index !== 0 ? firstItemStyle : styles.tokenLabel; return ( - {token.symbol} + {tokenInfo.symbol} ); })} @@ -41,6 +41,6 @@ export const TopTokens: React.StatelessComponent = (props: TopTo ); }; -function tokenLinkFromToken(token: Token, networkId: number) { - return sharedUtils.getEtherScanLinkIfExists(token.address, networkId, EtherscanLinkSuffixes.Address); +function tokenLinkFromToken(tokenInfo: WebsiteBackendTokenInfo, networkId: number) { + return sharedUtils.getEtherScanLinkIfExists(tokenInfo.address, networkId, EtherscanLinkSuffixes.Address); } diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 2754075a7..f6752aef1 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -503,19 +503,26 @@ export interface TokenState { price?: BigNumber; } -// TODO: Add topTokens property once available from backend export interface WebsiteBackendRelayerInfo { name: string; dailyTxnVolume: string; url: string; appUrl?: string; headerImgUrl: string; + topTokens: WebsiteBackendTokenInfo[]; } export interface WebsiteBackendPriceInfo { [symbol: string]: string; } +export interface WebsiteBackendTokenInfo { + address: string; + decimals: number; + name: string; + symbol: string; +} + export interface WebsiteBackendGasInfo { average: number; } -- cgit From 9f2258ebd962f60dfaa277814a3ead93d1b19ac7 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Wed, 9 May 2018 09:28:57 -0700 Subject: Change relayer grid tile to link on header --- .../components/relayer_index/relayer_grid_tile.tsx | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index 677c66695..cf2a1f66d 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -65,25 +65,25 @@ const styles: Styles = { export const RelayerGridTile: React.StatelessComponent = (props: RelayerGridTileProps) => { const link = props.relayerInfo.appUrl || props.relayerInfo.url; return ( - - -
+ +
+ -
-
- {props.relayerInfo.name} -
-
{props.relayerInfo.dailyTxnVolume}
-
- Daily Trade Volume -
- -
- Top tokens -
+
+
+
+ {props.relayerInfo.name} +
+
{props.relayerInfo.dailyTxnVolume}
+
+ Daily Trade Volume +
+ +
+ Top tokens
- - +
+ ); }; -- cgit From 09b26645eebb53e8a730f60998fd689c6bb15528 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Wed, 9 May 2018 10:04:28 -0700 Subject: Fallback image for relayer grid tile --- packages/website/ts/components/relayer_index/relayer_grid_tile.tsx | 3 ++- packages/website/ts/types.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index cf2a1f66d..23c5da50b 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -64,11 +64,12 @@ const styles: Styles = { export const RelayerGridTile: React.StatelessComponent = (props: RelayerGridTileProps) => { const link = props.relayerInfo.appUrl || props.relayerInfo.url; + const headerImgUrl = props.relayerInfo.headerImgUrl || '/images/landing/hero_chip_image.png'; return (
- +
diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index f6752aef1..67e4a5d7d 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -508,7 +508,7 @@ export interface WebsiteBackendRelayerInfo { dailyTxnVolume: string; url: string; appUrl?: string; - headerImgUrl: string; + headerImgUrl?: string; topTokens: WebsiteBackendTokenInfo[]; } -- cgit From 20f6d8d3d0d7452bd4dc35c7c55b2cfc169216ca Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Wed, 9 May 2018 10:57:02 -0700 Subject: Implement loading and error state for relayer grid --- .../ts/components/relayer_index/relayer_index.tsx | 60 ++++++++++++++++++---- 1 file changed, 50 insertions(+), 10 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index bc84cf9e8..df208753b 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -1,5 +1,7 @@ import { colors, Styles } from '@0xproject/react-shared'; import * as _ from 'lodash'; +import CircularProgress from 'material-ui/CircularProgress'; +import FlatButton from 'material-ui/FlatButton'; import { GridList } from 'material-ui/GridList'; import * as React from 'react'; @@ -55,7 +57,24 @@ export class RelayerIndex extends React.Component +
+
+ {_.isUndefined(this.state.error) ? ( + + ) : ( + + )} +
+
+
+ ); + } else { return (
- {this.state.relayerInfos.map((relayerInfo: WebsiteBackendRelayerInfo) => ( - + {this.state.relayerInfos.map((relayerInfo: WebsiteBackendRelayerInfo, index) => ( + ))}
); - } else { - // TODO: loading and error states with a scrolling container - return null; } } private async _fetchRelayerInfosAsync(): Promise { @@ -96,3 +108,31 @@ export class RelayerIndex extends React.Component void; +} +const Retry = (props: RetryProps) => ( +
+
+
+ Something went wrong. +
+
+ +
+
+
+); -- cgit From e83b056bd494e0e6a16fd0b9bfdb84bac2ab271d Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Wed, 9 May 2018 15:59:10 -0700 Subject: Clear relayer grid state when fetching --- packages/website/ts/components/relayer_index/relayer_index.tsx | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index df208753b..dffd0f83f 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -93,6 +93,12 @@ export class RelayerIndex extends React.Component { try { + if (!this._isUnmounted) { + this.setState({ + relayerInfos: undefined, + error: undefined, + }); + } const relayerInfos = await backendClient.getRelayerInfosAsync(); if (!this._isUnmounted) { this.setState({ -- cgit From c64ad1af28ef116e210aafb3ea6ad2138361cd7c Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Wed, 9 May 2018 17:29:02 -0700 Subject: Add fallback image support to relayer grid tile --- .../components/relayer_index/relayer_grid_tile.tsx | 38 ++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index 23c5da50b..d88a59d15 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -62,14 +62,19 @@ const styles: Styles = { }, }; +const FALLBACK_IMG_SRC = '/images/landing/hero_chip_image.png'; + export const RelayerGridTile: React.StatelessComponent = (props: RelayerGridTileProps) => { const link = props.relayerInfo.appUrl || props.relayerInfo.url; - const headerImgUrl = props.relayerInfo.headerImgUrl || '/images/landing/hero_chip_image.png'; return (
- +
@@ -88,3 +93,32 @@ export const RelayerGridTile: React.StatelessComponent = ( ); }; + +interface ImgWithFallbackProps { + src?: string; + fallbackSrc: string; + style: React.CSSProperties; +} +interface ImgWithFallbackState { + imageLoadFailed: boolean; +} +class ImgWithFallback extends React.Component { + constructor(props: ImgWithFallbackProps) { + super(props); + this.state = { + imageLoadFailed: false, + }; + } + public render() { + if (this.state.imageLoadFailed || _.isUndefined(this.props.src)) { + return ; + } else { + return ; + } + } + private _onError() { + this.setState({ + imageLoadFailed: true, + }); + } +} -- cgit