From b49148ec54886b11042e42eed9ad4e8793840775 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 24 May 2018 10:11:56 -0700 Subject: Implement metamask installation flow --- .../ts/components/onboarding/onboarding_flow.tsx | 7 +++ .../onboarding/portal_onboarding_flow.tsx | 60 ++++++++++++++++++---- .../ts/components/top_bar/provider_display.tsx | 9 ++-- .../components/wallet/wallet_disconnected_item.tsx | 4 +- .../ts/containers/portal_onboarding_flow.ts | 10 +++- packages/website/ts/utils/utils.ts | 4 ++ 6 files changed, 75 insertions(+), 19 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index 621d14260..22fc71481 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -17,9 +17,16 @@ const joyrideStyleOptions: StyleOptions = { // Wrapper around Joyride with defaults and styles set export class OnboardingFlow extends React.Component { + private _joyrideRef: React.RefObject; + constructor(props: OnboardingFlowProps) { + super(props); + this._joyrideRef = React.createRef(); + } + public render(): React.ReactNode { return ( void; + userAddress: string; + providerType: ProviderType; + injectedProviderName: string; + blockchainIsLoaded: boolean; + setOnboardingStep: (stepIndex: number) => void; } -const steps: Step[] = [ - { - target: '.wallet', - content: 'You are onboarding right now!', - placement: 'right', - disableBeacon: true, - }, -]; - export class PortalOnboardingFlow extends React.Component { public render(): React.ReactNode { return ( ); } + + private _isAddressAvailable(): boolean { + return !_.isEmpty(this.props.userAddress); + } + + private _getSteps(): Step[] { + const allSteps: Step[] = [ + { + target: '.wallet', + content: + 'Before you begin, you need to connect to a wallet. This will be used across all 0x relayers and dApps', + placement: 'right', + disableBeacon: true, + }, + { + target: '.wallet', + content: 'Unlock your metamask extension to begin', + placement: 'right', + disableBeacon: true, + }, + { + target: '.wallet', + content: + 'In order to start trading on any 0x relayer in the 0x ecosystem, you need to complete two simple steps', + placement: 'right', + disableBeacon: true, + }, + ]; + const [noMetamaskStep, lockedMetamaskStep, ...restOfSteps] = allSteps; + if (this._isAddressAvailable()) { + return restOfSteps; + } + const isExternallyInjected = utils.isExternallyInjected( + this.props.providerType, + this.props.injectedProviderName, + ); + if (isExternallyInjected) { + return [lockedMetamaskStep, ...restOfSteps]; + } + return allSteps; + } } diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx index fc516882a..4dc37f231 100644 --- a/packages/website/ts/components/top_bar/provider_display.tsx +++ b/packages/website/ts/components/top_bar/provider_display.tsx @@ -39,8 +39,7 @@ const styles: Styles = { export class ProviderDisplay extends React.Component { public render(): React.ReactNode { const isAddressAvailable = !_.isEmpty(this.props.userAddress); - const isExternallyInjectedProvider = - this.props.providerType === ProviderType.Injected && this.props.injectedProviderName !== '0x Public'; + const isExternallyInjectedProvider = utils.isExternallyInjected(this.props.providerType, this.props.injectedProviderName); const displayAddress = isAddressAvailable ? utils.getAddressBeginAndEnd(this.props.userAddress) : isExternallyInjectedProvider @@ -69,15 +68,13 @@ export class ProviderDisplay extends React.Component ); - const hasInjectedProvider = - this.props.injectedProviderName !== '0x Public' && this.props.providerType === ProviderType.Injected; const hasLedgerProvider = this.props.providerType === ProviderType.Ledger; - const horizontalPosition = hasInjectedProvider || hasLedgerProvider ? 'left' : 'middle'; + const horizontalPosition = isExternallyInjectedProvider || hasLedgerProvider ? 'left' : 'middle'; return (
= ( props: WalletDisconnectedItemProps, ) => { - const isExternallyInjectedProvider = - props.providerType === ProviderType.Injected && props.injectedProviderName !== '0x Public'; + const isExternallyInjectedProvider = utils.isExternallyInjected(props.providerType, props.injectedProviderName); return (
diff --git a/packages/website/ts/containers/portal_onboarding_flow.ts b/packages/website/ts/containers/portal_onboarding_flow.ts index 3cd4e8510..3ce91b7b3 100644 --- a/packages/website/ts/containers/portal_onboarding_flow.ts +++ b/packages/website/ts/containers/portal_onboarding_flow.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; -import { ActionTypes } from 'ts/types'; +import { ActionTypes, ProviderType } from 'ts/types'; import { PortalOnboardingFlow as PortalOnboardingFlowComponent } from 'ts/components/onboarding/portal_onboarding_flow'; import { State } from 'ts/redux/reducer'; @@ -11,6 +11,10 @@ interface PortalOnboardingFlowProps {} interface ConnectedState { stepIndex: number; isRunning: boolean; + userAddress: string; + providerType: ProviderType; + injectedProviderName: string; + blockchainIsLoaded: boolean; } interface ConnectedDispatch { @@ -20,6 +24,10 @@ interface ConnectedDispatch { const mapStateToProps = (state: State): ConnectedState => ({ stepIndex: state.portalOnboardingStep, isRunning: state.isPortalOnboardingShowing, + userAddress: state.userAddress, + providerType: state.providerType, + injectedProviderName: state.injectedProviderName, + blockchainIsLoaded: state.blockchainIsLoaded, }); const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index e79a873e0..1d4f7eb5c 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -13,6 +13,7 @@ import { Providers, ScreenWidths, Side, + ProviderType, SideToAssetToken, Token, TokenByAddress, @@ -313,6 +314,9 @@ export const utils = { isStaging(): boolean { return _.includes(window.location.href, configs.DOMAIN_STAGING); }, + isExternallyInjected(providerType: ProviderType, injectedProviderName: string): boolean { + return providerType === ProviderType.Injected && injectedProviderName !== constants.PROVIDER_NAME_PUBLIC; + }, isDogfood, shouldShowPortalV2(): boolean { return this.isDevelopment() || this.isStaging() || this.isDogfood(); -- cgit From f0af6388748bdf08ae81e079e497dc879703c97d Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 24 May 2018 11:44:20 -0700 Subject: Fix PortalOnboardingFlowProps --- packages/website/ts/components/onboarding/portal_onboarding_flow.tsx | 1 - 1 file changed, 1 deletion(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index 7abfdabda..feaa9c997 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -14,7 +14,6 @@ export interface PortalOnboardingFlowProps { providerType: ProviderType; injectedProviderName: string; blockchainIsLoaded: boolean; - setOnboardingStep: (stepIndex: number) => void; } export class PortalOnboardingFlow extends React.Component { -- cgit From 809ac3340cb477d4b46e24d4bad028568beeec06 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 24 May 2018 17:10:39 -0700 Subject: Add ability to blacklist onboarding steps --- .../ts/components/onboarding/onboarding_flow.tsx | 56 +++++++++++++++++-- .../onboarding/portal_onboarding_flow.tsx | 63 ++++++++++++---------- .../ts/containers/portal_onboarding_flow.ts | 7 +++ 3 files changed, 92 insertions(+), 34 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index 22fc71481..0cb640f8f 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -6,9 +6,11 @@ import { zIndex } from 'ts/utils/style'; export interface OnboardingFlowProps { steps: Step[]; + blacklistedStepIndices: number[]; stepIndex: number; isRunning: boolean; onClose: () => void; + setOnboardingStep: (stepIndex: number) => void; } const joyrideStyleOptions: StyleOptions = { @@ -17,16 +19,17 @@ const joyrideStyleOptions: StyleOptions = { // Wrapper around Joyride with defaults and styles set export class OnboardingFlow extends React.Component { - private _joyrideRef: React.RefObject; - constructor(props: OnboardingFlowProps) { - super(props); - this._joyrideRef = React.createRef(); + public componentDidMount(): void { + this._setOnboardingStepBasedOnBlacklist(this.props.stepIndex); + } + + public componentWillReceiveProps(nextProps: OnboardingFlowProps): void { + this._setOnboardingStepBasedOnBlacklist(nextProps.stepIndex); } public render(): React.ReactNode { return ( { ); } + private _setOnboardingStepBasedOnBlacklist(nextIndex: number): void { + const blacklistedSteps = this.props.blacklistedStepIndices; + const newStepIndex = this._adjustedStepBasedOnBlacklist( + this.props.stepIndex, + nextIndex, + this.props.steps.length, + blacklistedSteps, + ); + this.props.setOnboardingStep(newStepIndex); + } + + private _adjustedStepBasedOnBlacklist( + currentStep: number, + nextStep: number, + totalSteps: number, + blacklistedSteps: number[], + ): number { + if (!blacklistedSteps.includes(nextStep)) { + return nextStep; + } + let newStep = nextStep; + const op = nextStep >= currentStep ? _.add : _.subtract; + let didSearch = false; + while (blacklistedSteps.includes(newStep)) { + newStep = op(newStep, 1); + if (newStep < 0) { + if (didSearch) { + break; + } + newStep = totalSteps - 1; + didSearch = true; + } + if (newStep >= totalSteps) { + if (didSearch) { + break; + } + newStep = 0; + didSearch = true; + } + } + return newStep; + } + private _handleChange(data: CallbackData): void { switch (data.action) { case 'close': diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index feaa9c997..c80d1dd6e 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -2,6 +2,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { Step } from 'react-joyride'; +import { black } from 'material-ui/styles/colors'; import { OnboardingFlow } from 'ts/components/onboarding/onboarding_flow'; import { ProviderType } from 'ts/types'; import { utils } from 'ts/utils/utils'; @@ -9,21 +10,47 @@ import { utils } from 'ts/utils/utils'; export interface PortalOnboardingFlowProps { stepIndex: number; isRunning: boolean; - onClose: () => void; userAddress: string; providerType: ProviderType; injectedProviderName: string; blockchainIsLoaded: boolean; + onClose: () => void; + setOnboardingStep: (stepIndex: number) => void; } +const steps: Step[] = [ + { + target: '.wallet', + content: + 'Before you begin, you need to connect to a wallet. This will be used across all 0x relayers and dApps', + placement: 'right', + disableBeacon: true, + }, + { + target: '.wallet', + content: 'Unlock your metamask extension to begin', + placement: 'right', + disableBeacon: true, + }, + { + target: '.wallet', + content: + 'In order to start trading on any 0x relayer in the 0x ecosystem, you need to complete two simple steps', + placement: 'right', + disableBeacon: true, + }, +]; + export class PortalOnboardingFlow extends React.Component { public render(): React.ReactNode { return ( ); } @@ -32,40 +59,18 @@ export class PortalOnboardingFlow extends React.Component void; + setOnboardingStep: (stepIndex: number) => void; } const mapStateToProps = (state: State): ConnectedState => ({ @@ -37,6 +38,12 @@ const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ data: false, }); }, + setOnboardingStep: (stepIndex: number): void => { + dispatch({ + type: ActionTypes.UpdatePortalOnboardingStep, + data: stepIndex, + }); + }, }); export const PortalOnboardingFlow: React.ComponentClass = connect( -- cgit From e575323c6013b89873b698a7f34da135a4b2c51f Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 25 May 2018 11:36:07 -0700 Subject: Auto-show onboarding flow on first page view --- .../ts/components/onboarding/portal_onboarding_flow.tsx | 17 +++++++++++++++-- .../website/ts/containers/portal_onboarding_flow.ts | 8 +++++--- packages/website/ts/index.tsx | 16 +++++++++++++++- packages/website/ts/local_storage/local_storage.ts | 14 ++++++++++++++ packages/website/ts/redux/reducer.ts | 5 ++++- 5 files changed, 53 insertions(+), 7 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index c80d1dd6e..ab9d58415 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -14,7 +14,8 @@ export interface PortalOnboardingFlowProps { providerType: ProviderType; injectedProviderName: string; blockchainIsLoaded: boolean; - onClose: () => void; + hasBeenSeen: boolean; + setIsRunning: (isRunning: boolean) => void; setOnboardingStep: (stepIndex: number) => void; } @@ -42,6 +43,12 @@ const steps: Step[] = [ ]; export class PortalOnboardingFlow extends React.Component { + public componentDidMount(): void { + this._autoStartOnboardingIfShould(); + } + public componentDidUpdate(): void { + this._autoStartOnboardingIfShould(); + } public render(): React.ReactNode { return ( ); @@ -73,4 +80,10 @@ export class PortalOnboardingFlow extends React.Component void; + setIsRunning: (isRunning: boolean) => void; setOnboardingStep: (stepIndex: number) => void; } @@ -29,13 +30,14 @@ const mapStateToProps = (state: State): ConnectedState => ({ providerType: state.providerType, injectedProviderName: state.injectedProviderName, blockchainIsLoaded: state.blockchainIsLoaded, + hasBeenSeen: state.hasPortalOnboardingBeenSeen, }); const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ - onClose: (): void => { + setIsRunning: (isRunning: boolean): void => { dispatch({ type: ActionTypes.UpdatePortalOnboardingShowing, - data: false, + data: isRunning, }); }, setOnboardingStep: (stepIndex: number): void => { diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index 4fe81a91e..5ab03c07c 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -1,4 +1,5 @@ // Polyfills +import * as _ from 'lodash'; import { MuiThemeProvider } from 'material-ui/styles'; import * as React from 'react'; import { render } from 'react-dom'; @@ -14,6 +15,7 @@ import { Landing } from 'ts/containers/landing'; import { NotFound } from 'ts/containers/not_found'; import { Wiki } from 'ts/containers/wiki'; import { createLazyComponent } from 'ts/lazy_component'; +import { stateStorage } from 'ts/local_storage/state_storage'; import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage'; import { tradeHistoryStorage } from 'ts/local_storage/trade_history_storage'; import { reducer, State } from 'ts/redux/reducer'; @@ -75,7 +77,19 @@ const LazyOrderUtilsDocumentation = createLazyComponent('Documentation', async ( analytics.init(); // tslint:disable-next-line:no-floating-promises analytics.logProviderAsync((window as any).web3); -const store: ReduxStore = createStore(reducer, devToolsEnhancer({ name: '0x Website Redux Store' })); +const store: ReduxStore = createStore( + reducer, + stateStorage.getPersistedDefaultState(), + devToolsEnhancer({ name: '0x Website Redux Store' }), +); +store.subscribe( + _.throttle(() => { + stateStorage.saveState({ + hasPortalOnboardingBeenSeen: store.getState().hasPortalOnboardingBeenSeen, + }); + }, 1000), +); + render(
diff --git a/packages/website/ts/local_storage/local_storage.ts b/packages/website/ts/local_storage/local_storage.ts index 20a533a91..569e262fc 100644 --- a/packages/website/ts/local_storage/local_storage.ts +++ b/packages/website/ts/local_storage/local_storage.ts @@ -26,6 +26,20 @@ export const localStorage = { } window.localStorage.removeItem(key); }, + getObject(key: string): object { + const item = localStorage.getItemIfExists(key); + if (item) { + try { + return JSON.parse(item); + } catch (err) { + return undefined; + } + } + return undefined; + }, + setObject(key: string, value: object): void { + localStorage.setItem(key, JSON.stringify(value)); + }, getAllKeys(): string[] { if (!this.doesExist) { return []; diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index e61345c87..5c57792f7 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -42,6 +42,7 @@ export interface State { userEtherBalanceInWei: BigNumber; portalOnboardingStep: number; isPortalOnboardingShowing: boolean; + hasPortalOnboardingBeenSeen: boolean; // Note: cache of supplied orderJSON in fill order step. Do not use for anything else. userSuppliedOrderCache: Order; @@ -56,7 +57,7 @@ export interface State { translate: Translate; } -const INITIAL_STATE: State = { +export const INITIAL_STATE: State = { // Portal blockchainErr: BlockchainErrs.NoError, blockchainIsLoaded: false, @@ -84,6 +85,7 @@ const INITIAL_STATE: State = { userSuppliedOrderCache: undefined, portalOnboardingStep: 0, isPortalOnboardingShowing: false, + hasPortalOnboardingBeenSeen: false, // Docs docsVersion: DEFAULT_DOCS_VERSION, availableDocVersions: [DEFAULT_DOCS_VERSION], @@ -309,6 +311,7 @@ export function reducer(state: State = INITIAL_STATE, action: Action): State { return { ...state, isPortalOnboardingShowing, + hasPortalOnboardingBeenSeen: true, }; } -- cgit From b0e6ce581a77d752d4a0b7f025237c4ac0aee010 Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 25 May 2018 12:02:18 -0700 Subject: Add next button --- packages/website/ts/components/onboarding/onboarding_flow.tsx | 6 +++++- .../website/ts/components/onboarding/portal_onboarding_flow.tsx | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index 0cb640f8f..68dfc11bf 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -31,6 +31,7 @@ export class OnboardingFlow extends React.Component { return ( { this.props.steps.length, blacklistedSteps, ); - this.props.setOnboardingStep(newStepIndex); + if (newStepIndex !== nextIndex) { + this.props.setOnboardingStep(newStepIndex); + } } private _adjustedStepBasedOnBlacklist( @@ -87,6 +90,7 @@ export class OnboardingFlow extends React.Component { switch (data.action) { case 'close': this.props.onClose(); + break; } } } diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index ab9d58415..99fe5006d 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -40,6 +40,12 @@ const steps: Step[] = [ placement: 'right', disableBeacon: true, }, + { + target: '.wallet', + content: 'Before you begin you will need to send some ETH to your metamask wallet', + placement: 'right', + disableBeacon: true, + }, ]; export class PortalOnboardingFlow extends React.Component { -- cgit From 9631927a8cbbc739f1e1d0d574c61e19803ee0dc Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 25 May 2018 12:02:45 -0700 Subject: Add localstorage helper --- packages/website/ts/local_storage/state_storage.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 packages/website/ts/local_storage/state_storage.ts (limited to 'packages/website') diff --git a/packages/website/ts/local_storage/state_storage.ts b/packages/website/ts/local_storage/state_storage.ts new file mode 100644 index 000000000..517784b5b --- /dev/null +++ b/packages/website/ts/local_storage/state_storage.ts @@ -0,0 +1,16 @@ +import { localStorage } from 'ts/local_storage/local_storage'; +import { INITIAL_STATE, State } from 'ts/redux/reducer'; + +const STORAGE_NAME = 'persistedState'; + +export const stateStorage = { + saveState(partialState: Partial): void { + localStorage.setObject(STORAGE_NAME, partialState); + }, + getPersistedState(): Partial { + return localStorage.getObject(STORAGE_NAME); + }, + getPersistedDefaultState(): State { + return { ...INITIAL_STATE, ...stateStorage.getPersistedState() }; + }, +}; -- cgit From 1026952f26043611c4aa3d77b2024cae304021e9 Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 25 May 2018 13:13:06 -0700 Subject: Run linter --- packages/website/ts/components/top_bar/provider_display.tsx | 5 ++++- packages/website/ts/utils/utils.ts | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx index 4dc37f231..1cead7a5d 100644 --- a/packages/website/ts/components/top_bar/provider_display.tsx +++ b/packages/website/ts/components/top_bar/provider_display.tsx @@ -39,7 +39,10 @@ const styles: Styles = { export class ProviderDisplay extends React.Component { public render(): React.ReactNode { const isAddressAvailable = !_.isEmpty(this.props.userAddress); - const isExternallyInjectedProvider = utils.isExternallyInjected(this.props.providerType, this.props.injectedProviderName); + const isExternallyInjectedProvider = utils.isExternallyInjected( + this.props.providerType, + this.props.injectedProviderName, + ); const displayAddress = isAddressAvailable ? utils.getAddressBeginAndEnd(this.props.userAddress) : isExternallyInjectedProvider diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index 1d4f7eb5c..c0d055070 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -11,9 +11,9 @@ import { Environments, Order, Providers, + ProviderType, ScreenWidths, Side, - ProviderType, SideToAssetToken, Token, TokenByAddress, -- cgit From 39008372e522bd0963dc71ec91235bb81813355f Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 25 May 2018 15:31:27 -0700 Subject: Write custom tooltip component --- .../ts/components/onboarding/onboarding_flow.tsx | 39 ++++++++++++++++++---- .../components/onboarding/onboarding_tooltip.tsx | 23 +++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 packages/website/ts/components/onboarding/onboarding_tooltip.tsx (limited to 'packages/website') diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index 68dfc11bf..4ed834916 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -2,6 +2,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import Joyride, { CallbackData, Step, StyleOptions } from 'react-joyride'; +import { OnboardingTooltip } from 'ts/components/onboarding/onboarding_tooltip'; import { zIndex } from 'ts/utils/style'; export interface OnboardingFlowProps { @@ -36,7 +37,7 @@ export class OnboardingFlow extends React.Component { steps={this.props.steps} stepIndex={this.props.stepIndex} styles={{ options: joyrideStyleOptions }} - callback={this._handleChange.bind(this)} + tooltipComponent={this._renderToolTip.bind(this)} /> ); } @@ -86,11 +87,37 @@ export class OnboardingFlow extends React.Component { return newStep; } - private _handleChange(data: CallbackData): void { - switch (data.action) { - case 'close': - this.props.onClose(); - break; + private _renderToolTip(): React.ReactNode { + const { steps, stepIndex } = this.props; + const step = steps[stepIndex]; + const isLastStep = steps.length - 1 === stepIndex; + return ( + + ); + } + + private _goToNextStep(): void { + const nextStep = this.props.stepIndex + 1; + if (nextStep < this.props.steps.length) { + this.props.setOnboardingStep(nextStep); + } else { + this.props.onClose(); + } + } + + private _goToPrevStep(): void { + const nextStep = this.props.stepIndex - 1; + if (nextStep >= 0) { + this.props.setOnboardingStep(nextStep); + } else { + this.props.onClose(); } } } diff --git a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx new file mode 100644 index 000000000..3ab18b011 --- /dev/null +++ b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; + +import { Island } from 'ts/components/ui/island'; + +export interface OnboardingTooltipProps { + title: string; + content: React.ReactNode; + isLastStep: boolean; + index: number; + onClose: () => void; + onClickNext: () => void; + onClickBack: () => void; +} + +export const OnboardingTooltip: React.StatelessComponent = (props: OnboardingTooltipProps) => ( + + {props.title} + {props.content} + + + + +); -- cgit From d4a366aeb13d8b3ea2ace86f5566ddf8d94bf9c2 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 29 May 2018 10:48:44 -0700 Subject: Replace react-joyride with react-popper --- packages/website/package.json | 14 ++--- packages/website/public/index.html | 68 +++++++++++----------- .../ts/components/onboarding/onboarding_flow.tsx | 31 +++++++--- .../components/onboarding/onboarding_tooltip.tsx | 3 +- 4 files changed, 63 insertions(+), 53 deletions(-) (limited to 'packages/website') diff --git a/packages/website/package.json b/packages/website/package.json index 69115be9a..0cc6dfac1 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -11,22 +11,19 @@ "clean": "shx rm -f public/bundle*", "lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'", "watch": "webpack-dev-server --content-base public --https", - "deploy_dogfood": - "npm run build; aws s3 sync ./public/. s3://dogfood.0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", - "deploy_staging": - "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", - "deploy_live": - "npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers" + "deploy_dogfood": "npm run build; aws s3 sync ./public/. s3://dogfood.0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", + "deploy_staging": "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", + "deploy_live": "npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers" }, "author": "Fabio Berger", "license": "Apache-2.0", "dependencies": { + "@0xproject/contract-wrappers": "^0.0.2", "@0xproject/react-docs": "^0.0.12", "@0xproject/react-shared": "^0.1.7", "@0xproject/subproviders": "^0.10.2", - "@0xproject/contract-wrappers": "^0.0.2", - "@0xproject/typescript-typings": "^0.3.2", "@0xproject/types": "0.7.0", + "@0xproject/typescript-typings": "^0.3.2", "@0xproject/utils": "^0.6.2", "@0xproject/web3-wrapper": "^0.6.4", "accounting": "^0.4.1", @@ -47,6 +44,7 @@ "react-dom": "15.6.1", "react-ga": "^2.4.1", "react-joyride": "^2.0.0-11", + "react-popper": "^1.0.0-beta.6", "react-redux": "^5.0.3", "react-router-dom": "^4.1.1", "react-scroll": "^1.5.2", diff --git a/packages/website/public/index.html b/packages/website/public/index.html index c28e4abf4..4c0985c71 100644 --- a/packages/website/public/index.html +++ b/packages/website/public/index.html @@ -26,53 +26,53 @@ + gtag('config', 'UA-98720122-1'); +
+ (function (d, s, id) { + var js, + fjs = d.getElementsByTagName(s)[0]; + if (d.getElementById(id)) return; + js = d.createElement(s); + js.id = id; + js.src = '//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.8&appId=1687545238205192'; + fjs.parentNode.insertBefore(js, fjs); + })(document, 'script', 'facebook-jssdk'); +
+ t._e = []; + t.ready = function (f) { + t._e.push(f); + }; + return t; + })(document, 'script', 'twitter-wjs'); + - + \ No newline at end of file diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index 4ed834916..cd6fe2195 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -1,6 +1,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import Joyride, { CallbackData, Step, StyleOptions } from 'react-joyride'; +import { Popper, PopperChildrenProps } from 'react-popper'; import { OnboardingTooltip } from 'ts/components/onboarding/onboarding_tooltip'; import { zIndex } from 'ts/utils/style'; @@ -29,16 +30,13 @@ export class OnboardingFlow extends React.Component { } public render(): React.ReactNode { + if (!this.props.isRunning) { + return null; + } return ( - + + {this._renderPopperChildren.bind(this)} + ); } @@ -87,6 +85,21 @@ export class OnboardingFlow extends React.Component { return newStep; } + private _getElementForStep(): Element { + const step = this.props.steps[this.props.stepIndex]; + return document.querySelector(step.target); + } + + private _renderPopperChildren(props: any): React.ReactNode { + const { arrowProps } = props; + return ( +
+ {this._renderToolTip()} +
+
+ ); + } + private _renderToolTip(): React.ReactNode { const { steps, stepIndex } = this.props; const step = steps[stepIndex]; diff --git a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx index 3ab18b011..3994b0546 100644 --- a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx +++ b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx @@ -3,10 +3,9 @@ import * as React from 'react'; import { Island } from 'ts/components/ui/island'; export interface OnboardingTooltipProps { - title: string; + title?: string; content: React.ReactNode; isLastStep: boolean; - index: number; onClose: () => void; onClickNext: () => void; onClickBack: () => void; -- cgit From 338e8be327731116a4a0308ba353266293013dce Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 29 May 2018 10:53:55 -0700 Subject: Add types for react-popper, remove types for react-joyride --- .../ts/components/onboarding/onboarding_flow.tsx | 26 +++++++++++++--------- .../onboarding/portal_onboarding_flow.tsx | 7 +----- 2 files changed, 16 insertions(+), 17 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index cd6fe2195..9c416c2e1 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -1,11 +1,17 @@ import * as _ from 'lodash'; import * as React from 'react'; -import Joyride, { CallbackData, Step, StyleOptions } from 'react-joyride'; -import { Popper, PopperChildrenProps } from 'react-popper'; +import { Placement, Popper, PopperChildrenProps } from 'react-popper'; import { OnboardingTooltip } from 'ts/components/onboarding/onboarding_tooltip'; import { zIndex } from 'ts/utils/style'; +export interface Step { + target: string; + title?: string; + content: React.ReactNode; + placement?: Placement; +} + export interface OnboardingFlowProps { steps: Step[]; blacklistedStepIndices: number[]; @@ -15,11 +21,6 @@ export interface OnboardingFlowProps { setOnboardingStep: (stepIndex: number) => void; } -const joyrideStyleOptions: StyleOptions = { - zIndex: zIndex.overlay, -}; - -// Wrapper around Joyride with defaults and styles set export class OnboardingFlow extends React.Component { public componentDidMount(): void { this._setOnboardingStepBasedOnBlacklist(this.props.stepIndex); @@ -34,7 +35,7 @@ export class OnboardingFlow extends React.Component { return null; } return ( - + {this._renderPopperChildren.bind(this)} ); @@ -86,11 +87,10 @@ export class OnboardingFlow extends React.Component { } private _getElementForStep(): Element { - const step = this.props.steps[this.props.stepIndex]; - return document.querySelector(step.target); + return document.querySelector(this._getCurrentStep().target); } - private _renderPopperChildren(props: any): React.ReactNode { + private _renderPopperChildren(props: PopperChildrenProps): React.ReactNode { const { arrowProps } = props; return (
@@ -116,6 +116,10 @@ export class OnboardingFlow extends React.Component { ); } + private _getCurrentStep(): Step { + return this.props.steps[this.props.stepIndex]; + } + private _goToNextStep(): void { const nextStep = this.props.stepIndex + 1; if (nextStep < this.props.steps.length) { diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index 99fe5006d..00d278ce0 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -1,9 +1,8 @@ import * as _ from 'lodash'; import * as React from 'react'; -import { Step } from 'react-joyride'; import { black } from 'material-ui/styles/colors'; -import { OnboardingFlow } from 'ts/components/onboarding/onboarding_flow'; +import { OnboardingFlow, Step } from 'ts/components/onboarding/onboarding_flow'; import { ProviderType } from 'ts/types'; import { utils } from 'ts/utils/utils'; @@ -25,26 +24,22 @@ const steps: Step[] = [ content: 'Before you begin, you need to connect to a wallet. This will be used across all 0x relayers and dApps', placement: 'right', - disableBeacon: true, }, { target: '.wallet', content: 'Unlock your metamask extension to begin', placement: 'right', - disableBeacon: true, }, { target: '.wallet', content: 'In order to start trading on any 0x relayer in the 0x ecosystem, you need to complete two simple steps', placement: 'right', - disableBeacon: true, }, { target: '.wallet', content: 'Before you begin you will need to send some ETH to your metamask wallet', placement: 'right', - disableBeacon: true, }, ]; -- cgit From f0bbf2cab0ca7aef47699a3954fb0444d5d6d233 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 29 May 2018 12:53:18 -0700 Subject: Improve tooltip look --- .../ts/components/onboarding/onboarding_flow.tsx | 9 ++++-- .../components/onboarding/onboarding_tooltip.tsx | 15 ++++++---- .../ts/components/top_bar/provider_display.tsx | 3 ++ packages/website/ts/components/ui/container.tsx | 15 +++++++--- packages/website/ts/components/ui/overlay.tsx | 34 ++++++++++++++++++++++ packages/website/ts/components/wallet/wallet.tsx | 3 ++ packages/website/ts/utils/style.ts | 3 +- 7 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 packages/website/ts/components/ui/overlay.tsx (limited to 'packages/website') diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index 9c416c2e1..8af1a3a3f 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import { Placement, Popper, PopperChildrenProps } from 'react-popper'; import { OnboardingTooltip } from 'ts/components/onboarding/onboarding_tooltip'; +import { Overlay } from 'ts/components/ui/overlay'; import { zIndex } from 'ts/utils/style'; export interface Step { @@ -35,9 +36,11 @@ export class OnboardingFlow extends React.Component { return null; } return ( - - {this._renderPopperChildren.bind(this)} - + + + {this._renderPopperChildren.bind(this)} + + ); } diff --git a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx index 3994b0546..2d316b6b7 100644 --- a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx +++ b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; +import { Container } from 'ts/components/ui/container'; import { Island } from 'ts/components/ui/island'; export interface OnboardingTooltipProps { @@ -13,10 +14,14 @@ export interface OnboardingTooltipProps { export const OnboardingTooltip: React.StatelessComponent = (props: OnboardingTooltipProps) => ( - {props.title} - {props.content} - - - + +
+ {props.title} + {props.content} + + + +
+
); diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx index 1cead7a5d..82c95aa6d 100644 --- a/packages/website/ts/components/top_bar/provider_display.tsx +++ b/packages/website/ts/components/top_bar/provider_display.tsx @@ -11,6 +11,7 @@ import { Dispatcher } from 'ts/redux/dispatcher'; import { ProviderType } from 'ts/types'; import { colors } from 'ts/utils/colors'; import { constants } from 'ts/utils/constants'; +import { zIndex } from 'ts/utils/style'; import { utils } from 'ts/utils/utils'; const ROOT_HEIGHT = 24; @@ -33,6 +34,8 @@ const styles: Styles = { backgroundColor: colors.white, borderRadius: ROOT_HEIGHT, boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, + zIndex: zIndex.aboveOverlay, + position: 'relative', }, }; diff --git a/packages/website/ts/components/ui/container.tsx b/packages/website/ts/components/ui/container.tsx index 07868608c..d577447b0 100644 --- a/packages/website/ts/components/ui/container.tsx +++ b/packages/website/ts/components/ui/container.tsx @@ -1,10 +1,17 @@ import * as React from 'react'; +type StringOrNum = string | number; + export interface ContainerProps { - marginTop?: string | number; - marginBottom?: string | number; - marginRight?: string | number; - marginLeft?: string | number; + marginTop?: StringOrNum; + marginBottom?: StringOrNum; + marginRight?: StringOrNum; + marginLeft?: StringOrNum; + paddingTop?: StringOrNum; + paddingBottom?: StringOrNum; + paddingRight?: StringOrNum; + paddingLeft?: StringOrNum; + maxWidth?: StringOrNum; children?: React.ReactNode; } diff --git a/packages/website/ts/components/ui/overlay.tsx b/packages/website/ts/components/ui/overlay.tsx new file mode 100644 index 000000000..bb2ed8e59 --- /dev/null +++ b/packages/website/ts/components/ui/overlay.tsx @@ -0,0 +1,34 @@ +import { colors } from '@0xproject/react-shared'; +import * as _ from 'lodash'; +import * as React from 'react'; + +import { zIndex } from 'ts/utils/style'; + +export interface OverlayProps { + children?: React.ReactNode; + style?: React.CSSProperties; + onClick?: () => void; +} + +const style: React.CSSProperties = { + position: 'fixed', + top: 0, + right: 0, + bottom: 0, + left: 0, + zIndex: zIndex.overlay, + backgroundColor: 'rgba(0, 0, 0, 0.6)', +}; + +export const Overlay: React.StatelessComponent = (props: OverlayProps) => ( +
+ {props.children} +
+); + +Overlay.defaultProps = { + style: {}, + onClick: _.noop, +}; + +Overlay.displayName = 'Overlay'; diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 39e591bac..eab986208 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -47,6 +47,7 @@ import { colors } from 'ts/utils/colors'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles'; +import { zIndex } from '../../utils/style'; export interface WalletProps { userAddress: string; @@ -85,6 +86,8 @@ interface AccessoryItemConfig { const styles: Styles = { root: { width: '100%', + zIndex: zIndex.aboveOverlay, + position: 'relative', }, headerItemInnerDiv: { paddingLeft: 65, diff --git a/packages/website/ts/utils/style.ts b/packages/website/ts/utils/style.ts index 51b6e4eb6..0411cdd91 100644 --- a/packages/website/ts/utils/style.ts +++ b/packages/website/ts/utils/style.ts @@ -1,4 +1,5 @@ export const zIndex = { topBar: 1100, - overlay: 1101, + overlay: 1105, + aboveOverlay: 1106, }; -- cgit From 92cb5e10be479ca0440b3c7ca91fe70a1ef94c03 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 29 May 2018 14:26:38 -0700 Subject: Some cleanup --- .../ts/components/onboarding/onboarding_flow.tsx | 23 +++++++++++----------- .../components/onboarding/onboarding_tooltip.tsx | 1 + 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index 8af1a3a3f..d53f63400 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import { Placement, Popper, PopperChildrenProps } from 'react-popper'; import { OnboardingTooltip } from 'ts/components/onboarding/onboarding_tooltip'; +import { Container } from 'ts/components/ui/container'; import { Overlay } from 'ts/components/ui/overlay'; import { zIndex } from 'ts/utils/style'; @@ -36,7 +37,7 @@ export class OnboardingFlow extends React.Component { return null; } return ( - + {this._renderPopperChildren.bind(this)} @@ -94,11 +95,9 @@ export class OnboardingFlow extends React.Component { } private _renderPopperChildren(props: PopperChildrenProps): React.ReactNode { - const { arrowProps } = props; return (
{this._renderToolTip()} -
); } @@ -108,14 +107,16 @@ export class OnboardingFlow extends React.Component { const step = steps[stepIndex]; const isLastStep = steps.length - 1 === stepIndex; return ( - + + + ); } diff --git a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx index 2d316b6b7..5e8102af0 100644 --- a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx +++ b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; +import { colors } from '@0xproject/react-shared'; import { Container } from 'ts/components/ui/container'; import { Island } from 'ts/components/ui/island'; -- cgit From 30ac5fcb5e97c1ae62045ecf84fcd75a2da1ecf2 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 29 May 2018 15:15:30 -0700 Subject: Remove blacklist concept --- .../ts/components/onboarding/onboarding_flow.tsx | 56 +--------------------- .../components/onboarding/onboarding_tooltip.tsx | 3 +- .../onboarding/portal_onboarding_flow.tsx | 25 ++++++---- .../ts/components/top_bar/provider_display.tsx | 2 - 4 files changed, 20 insertions(+), 66 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index d53f63400..de734318e 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -12,11 +12,11 @@ export interface Step { title?: string; content: React.ReactNode; placement?: Placement; + hideBackButton?: boolean; } export interface OnboardingFlowProps { steps: Step[]; - blacklistedStepIndices: number[]; stepIndex: number; isRunning: boolean; onClose: () => void; @@ -24,14 +24,6 @@ export interface OnboardingFlowProps { } export class OnboardingFlow extends React.Component { - public componentDidMount(): void { - this._setOnboardingStepBasedOnBlacklist(this.props.stepIndex); - } - - public componentWillReceiveProps(nextProps: OnboardingFlowProps): void { - this._setOnboardingStepBasedOnBlacklist(nextProps.stepIndex); - } - public render(): React.ReactNode { if (!this.props.isRunning) { return null; @@ -45,51 +37,6 @@ export class OnboardingFlow extends React.Component { ); } - private _setOnboardingStepBasedOnBlacklist(nextIndex: number): void { - const blacklistedSteps = this.props.blacklistedStepIndices; - const newStepIndex = this._adjustedStepBasedOnBlacklist( - this.props.stepIndex, - nextIndex, - this.props.steps.length, - blacklistedSteps, - ); - if (newStepIndex !== nextIndex) { - this.props.setOnboardingStep(newStepIndex); - } - } - - private _adjustedStepBasedOnBlacklist( - currentStep: number, - nextStep: number, - totalSteps: number, - blacklistedSteps: number[], - ): number { - if (!blacklistedSteps.includes(nextStep)) { - return nextStep; - } - let newStep = nextStep; - const op = nextStep >= currentStep ? _.add : _.subtract; - let didSearch = false; - while (blacklistedSteps.includes(newStep)) { - newStep = op(newStep, 1); - if (newStep < 0) { - if (didSearch) { - break; - } - newStep = totalSteps - 1; - didSearch = true; - } - if (newStep >= totalSteps) { - if (didSearch) { - break; - } - newStep = 0; - didSearch = true; - } - } - return newStep; - } - private _getElementForStep(): Element { return document.querySelector(this._getCurrentStep().target); } @@ -112,6 +59,7 @@ export class OnboardingFlow extends React.Component { title={step.title} content={step.content} isLastStep={isLastStep} + hideBackButton={step.hideBackButton} onClose={this.props.onClose} onClickNext={this._goToNextStep.bind(this)} onClickBack={this._goToPrevStep.bind(this)} diff --git a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx index 5e8102af0..41925a672 100644 --- a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx +++ b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx @@ -11,6 +11,7 @@ export interface OnboardingTooltipProps { onClose: () => void; onClickNext: () => void; onClickBack: () => void; + hideBackButton?: boolean; } export const OnboardingTooltip: React.StatelessComponent = (props: OnboardingTooltipProps) => ( @@ -19,7 +20,7 @@ export const OnboardingTooltip: React.StatelessComponent
{props.title} {props.content} - + {!props.hideBackButton && }
diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index 00d278ce0..25570b4a7 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -35,6 +35,7 @@ const steps: Step[] = [ content: 'In order to start trading on any 0x relayer in the 0x ecosystem, you need to complete two simple steps', placement: 'right', + hideBackButton: true, }, { target: '.wallet', @@ -45,16 +46,15 @@ const steps: Step[] = [ export class PortalOnboardingFlow extends React.Component { public componentDidMount(): void { - this._autoStartOnboardingIfShould(); + this._overrideOnboardingStateIfShould(); } public componentDidUpdate(): void { - this._autoStartOnboardingIfShould(); + this._overrideOnboardingStateIfShould(); } public render(): React.ReactNode { return ( Date: Tue, 29 May 2018 15:27:30 -0700 Subject: Remove react-joyride from deps --- packages/website/package.json | 1 - 1 file changed, 1 deletion(-) (limited to 'packages/website') diff --git a/packages/website/package.json b/packages/website/package.json index 0cc6dfac1..95804f988 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -43,7 +43,6 @@ "react-document-title": "^2.0.3", "react-dom": "15.6.1", "react-ga": "^2.4.1", - "react-joyride": "^2.0.0-11", "react-popper": "^1.0.0-beta.6", "react-redux": "^5.0.3", "react-router-dom": "^4.1.1", -- cgit From bc28a08dd0d4db99549122fb4912b4931a3aad78 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 29 May 2018 15:49:13 -0700 Subject: Address PR feedback --- .../ts/components/onboarding/onboarding_flow.tsx | 6 +++--- .../onboarding/portal_onboarding_flow.tsx | 16 ++++++++-------- packages/website/ts/components/wallet/wallet.tsx | 2 +- .../website/ts/containers/portal_onboarding_flow.ts | 8 ++++---- packages/website/ts/index.tsx | 20 ++------------------ packages/website/ts/local_storage/local_storage.ts | 8 ++------ packages/website/ts/redux/store.ts | 21 +++++++++++++++++++++ 7 files changed, 41 insertions(+), 40 deletions(-) create mode 100644 packages/website/ts/redux/store.ts (limited to 'packages/website') diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index de734318e..524587358 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -20,7 +20,7 @@ export interface OnboardingFlowProps { stepIndex: number; isRunning: boolean; onClose: () => void; - setOnboardingStep: (stepIndex: number) => void; + updateOnboardingStep: (stepIndex: number) => void; } export class OnboardingFlow extends React.Component { @@ -75,7 +75,7 @@ export class OnboardingFlow extends React.Component { private _goToNextStep(): void { const nextStep = this.props.stepIndex + 1; if (nextStep < this.props.steps.length) { - this.props.setOnboardingStep(nextStep); + this.props.updateOnboardingStep(nextStep); } else { this.props.onClose(); } @@ -84,7 +84,7 @@ export class OnboardingFlow extends React.Component { private _goToPrevStep(): void { const nextStep = this.props.stepIndex - 1; if (nextStep >= 0) { - this.props.setOnboardingStep(nextStep); + this.props.updateOnboardingStep(nextStep); } else { this.props.onClose(); } diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index 25570b4a7..8bcb4c749 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -14,8 +14,8 @@ export interface PortalOnboardingFlowProps { injectedProviderName: string; blockchainIsLoaded: boolean; hasBeenSeen: boolean; - setIsRunning: (isRunning: boolean) => void; - setOnboardingStep: (stepIndex: number) => void; + updateIsRunning: (isRunning: boolean) => void; + updateOnboardingStep: (stepIndex: number) => void; } const steps: Step[] = [ @@ -57,8 +57,8 @@ export class PortalOnboardingFlow extends React.Component ); } @@ -75,7 +75,7 @@ export class PortalOnboardingFlow extends React.Component void; - setOnboardingStep: (stepIndex: number) => void; + updateIsRunning: (isRunning: boolean) => void; + updateOnboardingStep: (stepIndex: number) => void; } const mapStateToProps = (state: State): ConnectedState => ({ @@ -34,13 +34,13 @@ const mapStateToProps = (state: State): ConnectedState => ({ }); const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ - setIsRunning: (isRunning: boolean): void => { + updateIsRunning: (isRunning: boolean): void => { dispatch({ type: ActionTypes.UpdatePortalOnboardingShowing, data: isRunning, }); }, - setOnboardingStep: (stepIndex: number): void => { + updateOnboardingStep: (stepIndex: number): void => { dispatch({ type: ActionTypes.UpdatePortalOnboardingStep, data: stepIndex, diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index 5ab03c07c..a6bfe7dd2 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -1,13 +1,9 @@ -// Polyfills -import * as _ from 'lodash'; import { MuiThemeProvider } from 'material-ui/styles'; import * as React from 'react'; import { render } from 'react-dom'; import { Provider } from 'react-redux'; import { BrowserRouter as Router, Redirect, Route, Switch } from 'react-router-dom'; import * as injectTapEventPlugin from 'react-tap-event-plugin'; -import { createStore, Store as ReduxStore } from 'redux'; -import { devToolsEnhancer } from 'redux-devtools-extension/developmentOnly'; import { Redirecter } from 'ts/components/redirecter'; import { About } from 'ts/containers/about'; import { FAQ } from 'ts/containers/faq'; @@ -15,14 +11,14 @@ import { Landing } from 'ts/containers/landing'; import { NotFound } from 'ts/containers/not_found'; import { Wiki } from 'ts/containers/wiki'; import { createLazyComponent } from 'ts/lazy_component'; -import { stateStorage } from 'ts/local_storage/state_storage'; import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage'; import { tradeHistoryStorage } from 'ts/local_storage/trade_history_storage'; -import { reducer, State } from 'ts/redux/reducer'; +import { store } from 'ts/redux/store'; import { WebsiteLegacyPaths, WebsitePaths } from 'ts/types'; import { analytics } from 'ts/utils/analytics'; import { muiTheme } from 'ts/utils/mui_theme'; import { utils } from 'ts/utils/utils'; +// Polyfills import 'whatwg-fetch'; injectTapEventPlugin(); @@ -77,18 +73,6 @@ const LazyOrderUtilsDocumentation = createLazyComponent('Documentation', async ( analytics.init(); // tslint:disable-next-line:no-floating-promises analytics.logProviderAsync((window as any).web3); -const store: ReduxStore = createStore( - reducer, - stateStorage.getPersistedDefaultState(), - devToolsEnhancer({ name: '0x Website Redux Store' }), -); -store.subscribe( - _.throttle(() => { - stateStorage.saveState({ - hasPortalOnboardingBeenSeen: store.getState().hasPortalOnboardingBeenSeen, - }); - }, 1000), -); render( diff --git a/packages/website/ts/local_storage/local_storage.ts b/packages/website/ts/local_storage/local_storage.ts index 569e262fc..1e3258ce0 100644 --- a/packages/website/ts/local_storage/local_storage.ts +++ b/packages/website/ts/local_storage/local_storage.ts @@ -26,14 +26,10 @@ export const localStorage = { } window.localStorage.removeItem(key); }, - getObject(key: string): object { + getObject(key: string): object | undefined { const item = localStorage.getItemIfExists(key); if (item) { - try { - return JSON.parse(item); - } catch (err) { - return undefined; - } + return JSON.parse(item); } return undefined; }, diff --git a/packages/website/ts/redux/store.ts b/packages/website/ts/redux/store.ts new file mode 100644 index 000000000..203f068a1 --- /dev/null +++ b/packages/website/ts/redux/store.ts @@ -0,0 +1,21 @@ +import * as _ from 'lodash'; +import { createStore, Store as ReduxStore } from 'redux'; +import { devToolsEnhancer } from 'redux-devtools-extension/developmentOnly'; +import { stateStorage } from 'ts/local_storage/state_storage'; +import { reducer, State } from 'ts/redux/reducer'; + +const ONE_SECOND = 1000; + +export const store: ReduxStore = createStore( + reducer, + stateStorage.getPersistedDefaultState(), + devToolsEnhancer({ name: '0x Website Redux Store' }), +); +store.subscribe( + _.throttle(() => { + // Persisted state + stateStorage.saveState({ + hasPortalOnboardingBeenSeen: store.getState().hasPortalOnboardingBeenSeen, + }); + }, ONE_SECOND), +); -- cgit From b20e40dd6fd9964876a0006efe8b879a9a1d2118 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 29 May 2018 16:00:42 -0700 Subject: Fix unselected relayer drawer item --- packages/website/ts/components/portal/drawer_menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/portal/drawer_menu.tsx b/packages/website/ts/components/portal/drawer_menu.tsx index 75c8ac6c2..ace11639a 100644 --- a/packages/website/ts/components/portal/drawer_menu.tsx +++ b/packages/website/ts/components/portal/drawer_menu.tsx @@ -36,7 +36,7 @@ export interface DrawerMenuProps { } export const DrawerMenu = (props: DrawerMenuProps) => { const relayerItemEntry = { - to: `${WebsitePaths.Portal}/`, + to: `${WebsitePaths.Portal}`, labelText: 'Relayer ecosystem', iconName: 'zmdi-portable-wifi', }; -- cgit From b14c3fe48dc8eb5bad6eefceac872754eec34ffe Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 29 May 2018 17:57:22 -0700 Subject: Onboarding: implement add ETH step, and stub for add WETH step --- .../ts/components/onboarding/onboarding_flow.tsx | 6 +- .../components/onboarding/onboarding_tooltip.tsx | 29 +++++++- .../onboarding/portal_onboarding_flow.tsx | 86 ++++++++++++++-------- packages/website/ts/components/wallet/wallet.tsx | 30 ++++---- .../ts/containers/portal_onboarding_flow.ts | 9 ++- packages/website/ts/utils/constants.ts | 3 + packages/website/ts/utils/utils.ts | 5 ++ 7 files changed, 121 insertions(+), 47 deletions(-) (limited to 'packages/website') diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx index 524587358..4066babaf 100644 --- a/packages/website/ts/components/onboarding/onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx @@ -2,7 +2,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { Placement, Popper, PopperChildrenProps } from 'react-popper'; -import { OnboardingTooltip } from 'ts/components/onboarding/onboarding_tooltip'; +import { ContinueButtonDisplay, OnboardingTooltip } from 'ts/components/onboarding/onboarding_tooltip'; import { Container } from 'ts/components/ui/container'; import { Overlay } from 'ts/components/ui/overlay'; import { zIndex } from 'ts/utils/style'; @@ -13,6 +13,8 @@ export interface Step { content: React.ReactNode; placement?: Placement; hideBackButton?: boolean; + hideNextButton?: boolean; + continueButtonDisplay?: ContinueButtonDisplay; } export interface OnboardingFlowProps { @@ -60,9 +62,11 @@ export class OnboardingFlow extends React.Component { content={step.content} isLastStep={isLastStep} hideBackButton={step.hideBackButton} + hideNextButton={step.hideNextButton} onClose={this.props.onClose} onClickNext={this._goToNextStep.bind(this)} onClickBack={this._goToPrevStep.bind(this)} + continueButtonDisplay={step.continueButtonDisplay} /> ); diff --git a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx index 41925a672..eb34a87f2 100644 --- a/packages/website/ts/components/onboarding/onboarding_tooltip.tsx +++ b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx @@ -4,6 +4,8 @@ import { colors } from '@0xproject/react-shared'; import { Container } from 'ts/components/ui/container'; import { Island } from 'ts/components/ui/island'; +export type ContinueButtonDisplay = 'enabled' | 'disabled'; + export interface OnboardingTooltipProps { title?: string; content: React.ReactNode; @@ -11,19 +13,44 @@ export interface OnboardingTooltipProps { onClose: () => void; onClickNext: () => void; onClickBack: () => void; + continueButtonDisplay?: ContinueButtonDisplay; hideBackButton?: boolean; + hideNextButton?: boolean; +} + +// TODO: Make this more general button. +export interface ContinueButtonProps { + display: ContinueButtonDisplay; + children?: string; + onClick: () => void; } +export const ContinueButton: React.StatelessComponent = (props: ContinueButtonProps) => { + const isDisabled = props.display === 'disabled'; + return ( + + ); +}; + export const OnboardingTooltip: React.StatelessComponent = (props: OnboardingTooltipProps) => (
{props.title} {props.content} + {props.continueButtonDisplay && ( + + Continue + + )} {!props.hideBackButton && } - + {!props.hideNextButton && }
); + +OnboardingTooltip.displayName = 'OnboardingTooltip'; diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index 8bcb4c749..edaeb3736 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -1,49 +1,25 @@ import * as _ from 'lodash'; import * as React from 'react'; -import { black } from 'material-ui/styles/colors'; +import { BigNumber } from '@0xproject/utils'; import { OnboardingFlow, Step } from 'ts/components/onboarding/onboarding_flow'; -import { ProviderType } from 'ts/types'; +import { ProviderType, TokenByAddress } from 'ts/types'; import { utils } from 'ts/utils/utils'; export interface PortalOnboardingFlowProps { stepIndex: number; isRunning: boolean; userAddress: string; + hasBeenSeen: boolean; providerType: ProviderType; injectedProviderName: string; blockchainIsLoaded: boolean; - hasBeenSeen: boolean; + userEthBalanceInWei: BigNumber; + tokenByAddress: TokenByAddress; updateIsRunning: (isRunning: boolean) => void; updateOnboardingStep: (stepIndex: number) => void; } -const steps: Step[] = [ - { - target: '.wallet', - content: - 'Before you begin, you need to connect to a wallet. This will be used across all 0x relayers and dApps', - placement: 'right', - }, - { - target: '.wallet', - content: 'Unlock your metamask extension to begin', - placement: 'right', - }, - { - target: '.wallet', - content: - 'In order to start trading on any 0x relayer in the 0x ecosystem, you need to complete two simple steps', - placement: 'right', - hideBackButton: true, - }, - { - target: '.wallet', - content: 'Before you begin you will need to send some ETH to your metamask wallet', - placement: 'right', - }, -]; - export class PortalOnboardingFlow extends React.Component { public componentDidMount(): void { this._overrideOnboardingStateIfShould(); @@ -54,7 +30,7 @@ export class PortalOnboardingFlow extends React.Component new BigNumber(0); + } + + private _userHasWeth(): boolean { + // TODO: https://app.asana.com/0/681385331277907/690722374136933 + return false; + } + private _overrideOnboardingStateIfShould(): void { this._autoStartOnboardingIfShould(); this._adjustStepIfShould(); diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 524dc1952..d72c5458f 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -131,9 +131,6 @@ const styles: Styles = { }; const ETHER_ICON_PATH = '/images/ether.png'; -const ETHER_TOKEN_SYMBOL = 'WETH'; -const ZRX_TOKEN_SYMBOL = 'ZRX'; -const ETHER_SYMBOL = 'ETH'; const ICON_DIMENSION = 24; const TOKEN_AMOUNT_DISPLAY_PRECISION = 3; const BODY_ITEM_KEY = 'BODY'; @@ -319,7 +316,7 @@ export class Wallet extends React.Component { const primaryText = this._renderAmount( this.props.userEtherBalanceInWei, constants.DECIMAL_PLACES_ETH, - ETHER_SYMBOL, + constants.ETHER_SYMBOL, ); const etherToken = this._getEthToken(); const etherPrice = this.state.trackedTokenStateByAddress[etherToken.address].price; @@ -338,13 +335,13 @@ export class Wallet extends React.Component { ? { ...walletItemStyles.focusedItem, ...styles.paddedItem } : { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem }; const key = ETHER_ITEM_KEY; - return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig); + return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig, 'eth-row'); } private _renderTokenRows(): React.ReactNode { const trackedTokens = this.props.trackedTokens; const trackedTokensStartingWithEtherToken = trackedTokens.sort( - firstBy((t: Token) => t.symbol !== ETHER_TOKEN_SYMBOL) - .thenBy((t: Token) => t.symbol !== ZRX_TOKEN_SYMBOL) + firstBy((t: Token) => t.symbol !== constants.ETHER_TOKEN_SYMBOL) + .thenBy((t: Token) => t.symbol !== constants.ZRX_TOKEN_SYMBOL) .thenBy('address'), ); return _.map(trackedTokensStartingWithEtherToken, this._renderTokenRow.bind(this)); @@ -359,7 +356,8 @@ export class Wallet extends React.Component { const icon = ; const primaryText = this._renderAmount(tokenState.balance, token.decimals, token.symbol); const secondaryText = this._renderValue(tokenState.balance, token.decimals, tokenState.price); - const wrappedEtherDirection = token.symbol === ETHER_TOKEN_SYMBOL ? Side.Receive : undefined; + const isWeth = token.symbol === constants.ETHER_TOKEN_SYMBOL; + const wrappedEtherDirection = isWeth ? Side.Receive : undefined; const accessoryItemConfig: AccessoryItemConfig = { wrappedEtherDirection, allowanceToggleConfig: { @@ -368,7 +366,14 @@ export class Wallet extends React.Component { }, }; const key = token.address; - return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig); + return this._renderBalanceRow( + key, + icon, + primaryText, + secondaryText, + accessoryItemConfig, + isWeth ? 'weth-row' : undefined, + ); } private _renderBalanceRow( key: string, @@ -376,6 +381,7 @@ export class Wallet extends React.Component { primaryText: React.ReactNode, secondaryText: React.ReactNode, accessoryItemConfig: AccessoryItemConfig, + className?: string, ): React.ReactNode { const shouldShowWrapEtherItem = !_.isUndefined(this.state.wrappedEtherDirection) && @@ -385,7 +391,7 @@ export class Wallet extends React.Component { : { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem }; const etherToken = this._getEthToken(); return ( -
+
{icon}
@@ -575,8 +581,6 @@ export class Wallet extends React.Component { }); } private _getEthToken(): Token { - const tokens = _.values(this.props.tokenByAddress); - const etherToken = _.find(tokens, { symbol: ETHER_TOKEN_SYMBOL }); - return etherToken; + return utils.getEthToken(this.props.tokenByAddress); } } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/containers/portal_onboarding_flow.ts b/packages/website/ts/containers/portal_onboarding_flow.ts index 56a1db553..84739192f 100644 --- a/packages/website/ts/containers/portal_onboarding_flow.ts +++ b/packages/website/ts/containers/portal_onboarding_flow.ts @@ -1,7 +1,8 @@ +import { BigNumber } from '@0xproject/utils'; import * as React from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; -import { ActionTypes, ProviderType } from 'ts/types'; +import { ActionTypes, ProviderType, TokenByAddress } from 'ts/types'; import { PortalOnboardingFlow as PortalOnboardingFlowComponent } from 'ts/components/onboarding/portal_onboarding_flow'; import { State } from 'ts/redux/reducer'; @@ -12,10 +13,12 @@ interface ConnectedState { stepIndex: number; isRunning: boolean; userAddress: string; + hasBeenSeen: boolean; providerType: ProviderType; injectedProviderName: string; blockchainIsLoaded: boolean; - hasBeenSeen: boolean; + userEthBalanceInWei: BigNumber; + tokenByAddress: TokenByAddress; } interface ConnectedDispatch { @@ -30,6 +33,8 @@ const mapStateToProps = (state: State): ConnectedState => ({ providerType: state.providerType, injectedProviderName: state.injectedProviderName, blockchainIsLoaded: state.blockchainIsLoaded, + userEthBalanceInWei: state.userEtherBalanceInWei, + tokenByAddress: state.tokenByAddress, hasBeenSeen: state.hasPortalOnboardingBeenSeen, }); diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index f8710b349..9dc1d492c 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -4,6 +4,9 @@ import { BigNumber } from '@0xproject/utils'; export const constants = { DECIMAL_PLACES_ETH: 18, DECIMAL_PLACES_ZRX: 18, + ETHER_TOKEN_SYMBOL: 'WETH', + ZRX_TOKEN_SYMBOL: 'ZRX', + ETHER_SYMBOL: 'ETH', GENESIS_ORDER_BLOCK_BY_NETWORK_ID: { 1: 4145578, 42: 3117574, diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index c0d055070..b9d962b75 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -321,4 +321,9 @@ export const utils = { shouldShowPortalV2(): boolean { return this.isDevelopment() || this.isStaging() || this.isDogfood(); }, + getEthToken(tokenByAddress: TokenByAddress): Token { + const tokens = _.values(tokenByAddress); + const etherToken = _.find(tokens, { symbol: constants.ETHER_TOKEN_SYMBOL }); + return etherToken; + }, }; -- cgit