aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2018-06-01 01:45:34 +0800
committerFabio Berger <me@fabioberger.com>2018-06-01 01:45:34 +0800
commit94ee82e076d85b64063b5c71be13b1ebe0bb8c10 (patch)
tree53524249da4c5b907e1489d8677a7cc1edf069a2 /packages/website
parent0beab9eec45508fb6163bd6c0fd3970f0b61a91d (diff)
parent5b31d0aa3635ea524fb42d73cd6c713887dfef6a (diff)
downloaddexon-0x-contracts-94ee82e076d85b64063b5c71be13b1ebe0bb8c10.tar.gz
dexon-0x-contracts-94ee82e076d85b64063b5c71be13b1ebe0bb8c10.tar.zst
dexon-0x-contracts-94ee82e076d85b64063b5c71be13b1ebe0bb8c10.zip
Merge branch 'v2-prototype' into refactor/order-utils/for-v2
* v2-prototype: (45 commits) Check length before accessing indices, add awaitTransactionSuccess where needed, and rename function Add back before/after snapshots for each test Rename Signer to Wallet, rename GAS_ESTIMATE to GAS_LIMIT Make preSigned and allowedValidators mappings public Change names of signature types Fix formatting and tests Make AssetProxyId last byte of assetData Add signer to txHash, allow approveValidator to be used with executeTransaction Update Whitelist Fix Exchange interface Increase block gas limit Use last byte of signature as signature type Remove TxOrigin signature type, modify whitelist to use Validator signature type Update Whitelist contract with comments, also require maker to be whitelisted Fix build Add example whitelist contract and minimum tests Add sample whitelist contract Add TxOrigin signature type and rearrange order of types Add approveValidator function Add Validator signature type ... # Conflicts: # packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol # packages/contracts/src/utils/types.ts # packages/contracts/test/exchange/transactions.ts # packages/order-utils/src/asset_proxy_utils.ts
Diffstat (limited to 'packages/website')
-rw-r--r--packages/website/package.json15
-rw-r--r--packages/website/public/index.html68
-rw-r--r--packages/website/ts/components/onboarding/onboarding_flow.tsx93
-rw-r--r--packages/website/ts/components/onboarding/onboarding_tooltip.tsx56
-rw-r--r--packages/website/ts/components/onboarding/portal_onboarding_flow.tsx119
-rw-r--r--packages/website/ts/components/portal/drawer_menu.tsx2
-rw-r--r--packages/website/ts/components/top_bar/provider_display.tsx13
-rw-r--r--packages/website/ts/components/ui/container.tsx15
-rw-r--r--packages/website/ts/components/ui/overlay.tsx34
-rw-r--r--packages/website/ts/components/wallet/wallet.tsx33
-rw-r--r--packages/website/ts/components/wallet/wallet_disconnected_item.tsx4
-rw-r--r--packages/website/ts/containers/portal_onboarding_flow.ts30
-rw-r--r--packages/website/ts/index.tsx8
-rw-r--r--packages/website/ts/local_storage/local_storage.ts10
-rw-r--r--packages/website/ts/local_storage/state_storage.ts16
-rw-r--r--packages/website/ts/redux/reducer.ts5
-rw-r--r--packages/website/ts/redux/store.ts21
-rw-r--r--packages/website/ts/utils/constants.ts3
-rw-r--r--packages/website/ts/utils/style.ts3
-rw-r--r--packages/website/ts/utils/utils.ts9
20 files changed, 445 insertions, 112 deletions
diff --git a/packages/website/package.json b/packages/website/package.json
index 69115be9a..95804f988 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",
@@ -46,7 +43,7 @@
"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",
"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 @@
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-98720122-1"></script>
<script>
- window.dataLayer = window.dataLayer || [];
-function gtag() {
- dataLayer.push(arguments);
-}
-gtag('js', new Date());
+ window.dataLayer = window.dataLayer || [];
+ function gtag() {
+ dataLayer.push(arguments);
+ }
+ gtag('js', new Date());
-gtag('config', 'UA-98720122-1');
-</script>
+ gtag('config', 'UA-98720122-1');
+ </script>
<!-- End Google Analytics -->
<!-- Facebook SDK -->
<div id="fb-root"></div>
<script>
- (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');
-</script>
+ (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');
+ </script>
<div id="app"></div>
<!-- End Facebook SDK -->
<!-- Twitter SDK -->
<script>
- window.twttr = (function(d, s, id) {
- var js,
- fjs = d.getElementsByTagName(s)[0],
- t = window.twttr || {};
- if (d.getElementById(id)) return t;
- js = d.createElement(s);
- js.id = id;
- js.src = 'https://platform.twitter.com/widgets.js';
- fjs.parentNode.insertBefore(js, fjs);
+ window.twttr = (function (d, s, id) {
+ var js,
+ fjs = d.getElementsByTagName(s)[0],
+ t = window.twttr || {};
+ if (d.getElementById(id)) return t;
+ js = d.createElement(s);
+ js.id = id;
+ js.src = 'https://platform.twitter.com/widgets.js';
+ fjs.parentNode.insertBefore(js, fjs);
- t._e = [];
- t.ready = function(f) {
- t._e.push(f);
- };
- return t;
-})(document, 'script', 'twitter-wjs');
-</script>
+ t._e = [];
+ t.ready = function (f) {
+ t._e.push(f);
+ };
+ return t;
+ })(document, 'script', 'twitter-wjs');
+ </script>
<!-- End Twitter SDK -->
<!-- Main -->
<script type="text/javascript" crossorigin="anonymous" src="/bundle.js" charset="utf-8"></script>
</body>
-</html>
+</html> \ 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 621d14260..4066babaf 100644
--- a/packages/website/ts/components/onboarding/onboarding_flow.tsx
+++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx
@@ -1,39 +1,96 @@
import * as _ from 'lodash';
import * as React from 'react';
-import Joyride, { CallbackData, Step, StyleOptions } from 'react-joyride';
+import { Placement, Popper, PopperChildrenProps } from 'react-popper';
+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';
+export interface Step {
+ target: string;
+ title?: string;
+ content: React.ReactNode;
+ placement?: Placement;
+ hideBackButton?: boolean;
+ hideNextButton?: boolean;
+ continueButtonDisplay?: ContinueButtonDisplay;
+}
+
export interface OnboardingFlowProps {
steps: Step[];
stepIndex: number;
isRunning: boolean;
onClose: () => void;
+ updateOnboardingStep: (stepIndex: number) => void;
}
-const joyrideStyleOptions: StyleOptions = {
- zIndex: zIndex.overlay,
-};
-
-// Wrapper around Joyride with defaults and styles set
export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
public render(): React.ReactNode {
+ if (!this.props.isRunning) {
+ return null;
+ }
+ return (
+ <Overlay>
+ <Popper referenceElement={this._getElementForStep()} placement={this._getCurrentStep().placement}>
+ {this._renderPopperChildren.bind(this)}
+ </Popper>
+ </Overlay>
+ );
+ }
+
+ private _getElementForStep(): Element {
+ return document.querySelector(this._getCurrentStep().target);
+ }
+
+ private _renderPopperChildren(props: PopperChildrenProps): React.ReactNode {
return (
- <Joyride
- run={this.props.isRunning}
- debug={true}
- steps={this.props.steps}
- stepIndex={this.props.stepIndex}
- styles={{ options: joyrideStyleOptions }}
- callback={this._handleChange.bind(this)}
- />
+ <div ref={props.ref} style={props.style} data-placement={props.placement}>
+ {this._renderToolTip()}
+ </div>
);
}
- private _handleChange(data: CallbackData): void {
- switch (data.action) {
- case 'close':
- this.props.onClose();
+ private _renderToolTip(): React.ReactNode {
+ const { steps, stepIndex } = this.props;
+ const step = steps[stepIndex];
+ const isLastStep = steps.length - 1 === stepIndex;
+ return (
+ <Container marginLeft="15px">
+ <OnboardingTooltip
+ title={step.title}
+ 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}
+ />
+ </Container>
+ );
+ }
+
+ 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) {
+ this.props.updateOnboardingStep(nextStep);
+ } else {
+ this.props.onClose();
+ }
+ }
+
+ private _goToPrevStep(): void {
+ const nextStep = this.props.stepIndex - 1;
+ if (nextStep >= 0) {
+ this.props.updateOnboardingStep(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..eb34a87f2
--- /dev/null
+++ b/packages/website/ts/components/onboarding/onboarding_tooltip.tsx
@@ -0,0 +1,56 @@
+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';
+
+export type ContinueButtonDisplay = 'enabled' | 'disabled';
+
+export interface OnboardingTooltipProps {
+ title?: string;
+ content: React.ReactNode;
+ isLastStep: boolean;
+ 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<ContinueButtonProps> = (props: ContinueButtonProps) => {
+ const isDisabled = props.display === 'disabled';
+ return (
+ <button disabled={isDisabled} onClick={isDisabled ? undefined : props.onClick}>
+ {props.children}
+ </button>
+ );
+};
+
+export const OnboardingTooltip: React.StatelessComponent<OnboardingTooltipProps> = (props: OnboardingTooltipProps) => (
+ <Island>
+ <Container paddingRight="30px" paddingLeft="30px" maxWidth={350} paddingTop="15px" paddingBottom="15px">
+ <div className="flex flex-column">
+ {props.title}
+ {props.content}
+ {props.continueButtonDisplay && (
+ <ContinueButton onClick={props.onClickNext} display={props.continueButtonDisplay}>
+ Continue
+ </ContinueButton>
+ )}
+ {!props.hideBackButton && <button onClick={props.onClickBack}>Back</button>}
+ {!props.hideNextButton && <button onClick={props.onClickNext}>Skip</button>}
+ <button onClick={props.onClose}>Close</button>
+ </div>
+ </Container>
+ </Island>
+);
+
+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 11684aaee..edaeb3736 100644
--- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
+++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
@@ -1,32 +1,123 @@
+import * as _ from 'lodash';
import * as React from 'react';
-import { Step } from 'react-joyride';
-import { OnboardingFlow } from 'ts/components/onboarding/onboarding_flow';
+import { BigNumber } from '@0xproject/utils';
+import { OnboardingFlow, Step } from 'ts/components/onboarding/onboarding_flow';
+import { ProviderType, TokenByAddress } from 'ts/types';
+import { utils } from 'ts/utils/utils';
export interface PortalOnboardingFlowProps {
stepIndex: number;
isRunning: boolean;
- onClose: () => void;
+ userAddress: string;
+ hasBeenSeen: boolean;
+ providerType: ProviderType;
+ injectedProviderName: string;
+ blockchainIsLoaded: boolean;
+ userEthBalanceInWei: BigNumber;
+ tokenByAddress: TokenByAddress;
+ updateIsRunning: (isRunning: boolean) => void;
+ updateOnboardingStep: (stepIndex: number) => void;
}
-const steps: Step[] = [
- {
- target: '.wallet',
- content: 'You are onboarding right now!',
- placement: 'right',
- disableBeacon: true,
- },
-];
-
export class PortalOnboardingFlow extends React.Component<PortalOnboardingFlowProps> {
+ public componentDidMount(): void {
+ this._overrideOnboardingStateIfShould();
+ }
+ public componentDidUpdate(): void {
+ this._overrideOnboardingStateIfShould();
+ }
public render(): React.ReactNode {
return (
<OnboardingFlow
- steps={steps}
+ steps={this._getSteps()}
stepIndex={this.props.stepIndex}
isRunning={this.props.isRunning}
- onClose={this.props.onClose}
+ onClose={this.props.updateIsRunning.bind(this, false)}
+ updateOnboardingStep={this.props.updateOnboardingStep}
/>
);
}
+
+ private _getSteps(): Step[] {
+ 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',
+ hideBackButton: true,
+ hideNextButton: true,
+ },
+ {
+ target: '.wallet',
+ content: 'Unlock your metamask extension to begin',
+ placement: 'right',
+ hideBackButton: true,
+ hideNextButton: 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',
+ hideBackButton: true,
+ continueButtonDisplay: 'enabled',
+ },
+ {
+ target: '.eth-row',
+ content: 'Before you begin you will need to send some ETH to your metamask wallet',
+ placement: 'right',
+ continueButtonDisplay: this._userHasEth() ? 'enabled' : 'disabled',
+ },
+ {
+ target: '.weth-row',
+ content: 'You need to convert some of your ETH into tradeable Wrapped ETH (WETH)',
+ placement: 'right',
+ continueButtonDisplay: this._userHasWeth() ? 'enabled' : 'disabled',
+ },
+ ];
+ return steps;
+ }
+
+ private _isAddressAvailable(): boolean {
+ return !_.isEmpty(this.props.userAddress);
+ }
+
+ private _userHasEth(): boolean {
+ return this.props.userEthBalanceInWei > new BigNumber(0);
+ }
+
+ private _userHasWeth(): boolean {
+ // TODO: https://app.asana.com/0/681385331277907/690722374136933
+ return false;
+ }
+
+ private _overrideOnboardingStateIfShould(): void {
+ this._autoStartOnboardingIfShould();
+ this._adjustStepIfShould();
+ }
+
+ private _adjustStepIfShould(): void {
+ if (this._isAddressAvailable()) {
+ if (this.props.stepIndex < 2) {
+ this.props.updateOnboardingStep(2);
+ }
+ return;
+ }
+ const isExternallyInjected = utils.isExternallyInjected(
+ this.props.providerType,
+ this.props.injectedProviderName,
+ );
+ if (isExternallyInjected) {
+ this.props.updateOnboardingStep(1);
+ return;
+ }
+ this.props.updateOnboardingStep(0);
+ }
+ private _autoStartOnboardingIfShould(): void {
+ if (!this.props.isRunning && !this.props.hasBeenSeen && this.props.blockchainIsLoaded) {
+ this.props.updateIsRunning(true);
+ }
+ }
}
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',
};
diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx
index fc516882a..679ec07dc 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;
@@ -39,8 +40,10 @@ const styles: Styles = {
export class ProviderDisplay extends React.Component<ProviderDisplayProps, ProviderDisplayState> {
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 +72,13 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
)}
</div>
);
- 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 (
<div style={{ width: 'fit-content', height: 48, float: 'right' }}>
<DropDown
hoverActiveNode={hoverActiveNode}
- popoverContent={this.renderPopoverContent(hasInjectedProvider, hasLedgerProvider)}
+ popoverContent={this.renderPopoverContent(isExternallyInjectedProvider, hasLedgerProvider)}
anchorOrigin={{ horizontal: horizontalPosition, vertical: 'bottom' }}
targetOrigin={{ horizontal: horizontalPosition, vertical: 'top' }}
zDepth={1}
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) => (
+ <div style={{ ...style, ...props.style }} onClick={props.onClick}>
+ {props.children}
+ </div>
+);
+
+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 10ee31619..30d1285f4 100644
--- a/packages/website/ts/components/wallet/wallet.tsx
+++ b/packages/website/ts/components/wallet/wallet.tsx
@@ -46,6 +46,7 @@ import {
import { backendClient } from 'ts/utils/backend_client';
import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
+import { zIndex } from 'ts/utils/style';
import { utils } from 'ts/utils/utils';
import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles';
@@ -88,6 +89,8 @@ interface AccessoryItemConfig {
const styles: Styles = {
root: {
width: '100%',
+ zIndex: zIndex.aboveOverlay,
+ position: 'relative',
},
headerItemInnerDiv: {
paddingLeft: 65,
@@ -129,9 +132,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';
@@ -322,7 +322,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
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;
@@ -341,13 +341,13 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
? { ...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));
@@ -362,7 +362,8 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
const icon = <TokenIcon token={token} diameter={ICON_DIMENSION} link={tokenLink} />;
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: {
@@ -371,7 +372,14 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
},
};
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,
@@ -379,6 +387,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
primaryText: React.ReactNode,
secondaryText: React.ReactNode,
accessoryItemConfig: AccessoryItemConfig,
+ className?: string,
): React.ReactNode {
const shouldShowWrapEtherItem =
!_.isUndefined(this.state.wrappedEtherDirection) &&
@@ -388,7 +397,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
: { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem };
const etherToken = this._getEthToken();
return (
- <div key={key} className="flex flex-column">
+ <div key={key} className={`flex flex-column ${className || ''}`}>
<div className="flex items-center" style={style}>
<div className="px2">{icon}</div>
<div className="flex-none pr2 pt2 pb2">
@@ -578,8 +587,6 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
});
}
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/components/wallet/wallet_disconnected_item.tsx b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx
index d334f1748..39a62e1fb 100644
--- a/packages/website/ts/components/wallet/wallet_disconnected_item.tsx
+++ b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx
@@ -6,6 +6,7 @@ import * as React from 'react';
import { ProviderType } from 'ts/types';
import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
+import { utils } from 'ts/utils/utils';
export interface WalletDisconnectedItemProps {
providerType: ProviderType;
@@ -38,8 +39,7 @@ const BUTTON_BOTTOM_PADDING = 80;
export const WalletDisconnectedItem: React.StatelessComponent<WalletDisconnectedItemProps> = (
props: WalletDisconnectedItemProps,
) => {
- const isExternallyInjectedProvider =
- props.providerType === ProviderType.Injected && props.injectedProviderName !== '0x Public';
+ const isExternallyInjectedProvider = utils.isExternallyInjected(props.providerType, props.injectedProviderName);
return (
<div className="flex flex-center">
<div className="mx-auto">
diff --git a/packages/website/ts/containers/portal_onboarding_flow.ts b/packages/website/ts/containers/portal_onboarding_flow.ts
index 3cd4e8510..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 } 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';
@@ -11,22 +12,43 @@ interface PortalOnboardingFlowProps {}
interface ConnectedState {
stepIndex: number;
isRunning: boolean;
+ userAddress: string;
+ hasBeenSeen: boolean;
+ providerType: ProviderType;
+ injectedProviderName: string;
+ blockchainIsLoaded: boolean;
+ userEthBalanceInWei: BigNumber;
+ tokenByAddress: TokenByAddress;
}
interface ConnectedDispatch {
- onClose: () => void;
+ updateIsRunning: (isRunning: boolean) => void;
+ updateOnboardingStep: (stepIndex: number) => void;
}
const mapStateToProps = (state: State): ConnectedState => ({
stepIndex: state.portalOnboardingStep,
isRunning: state.isPortalOnboardingShowing,
+ userAddress: state.userAddress,
+ providerType: state.providerType,
+ injectedProviderName: state.injectedProviderName,
+ blockchainIsLoaded: state.blockchainIsLoaded,
+ userEthBalanceInWei: state.userEtherBalanceInWei,
+ tokenByAddress: state.tokenByAddress,
+ hasBeenSeen: state.hasPortalOnboardingBeenSeen,
});
const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
- onClose: (): void => {
+ updateIsRunning: (isRunning: boolean): void => {
dispatch({
type: ActionTypes.UpdatePortalOnboardingShowing,
- data: false,
+ data: isRunning,
+ });
+ },
+ 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 4fe81a91e..a6bfe7dd2 100644
--- a/packages/website/ts/index.tsx
+++ b/packages/website/ts/index.tsx
@@ -1,12 +1,9 @@
-// Polyfills
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';
@@ -16,11 +13,12 @@ import { Wiki } from 'ts/containers/wiki';
import { createLazyComponent } from 'ts/lazy_component';
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();
@@ -75,7 +73,7 @@ const LazyOrderUtilsDocumentation = createLazyComponent('Documentation', async (
analytics.init();
// tslint:disable-next-line:no-floating-promises
analytics.logProviderAsync((window as any).web3);
-const store: ReduxStore<State> = createStore(reducer, devToolsEnhancer({ name: '0x Website Redux Store' }));
+
render(
<Router>
<div>
diff --git a/packages/website/ts/local_storage/local_storage.ts b/packages/website/ts/local_storage/local_storage.ts
index 20a533a91..1e3258ce0 100644
--- a/packages/website/ts/local_storage/local_storage.ts
+++ b/packages/website/ts/local_storage/local_storage.ts
@@ -26,6 +26,16 @@ export const localStorage = {
}
window.localStorage.removeItem(key);
},
+ getObject(key: string): object | undefined {
+ const item = localStorage.getItemIfExists(key);
+ if (item) {
+ return JSON.parse(item);
+ }
+ 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/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<State>): void {
+ localStorage.setObject(STORAGE_NAME, partialState);
+ },
+ getPersistedState(): Partial<State> {
+ return localStorage.getObject(STORAGE_NAME);
+ },
+ getPersistedDefaultState(): State {
+ return { ...INITIAL_STATE, ...stateStorage.getPersistedState() };
+ },
+};
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,
};
}
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<State> = createStore(
+ reducer,
+ stateStorage.getPersistedDefaultState(),
+ devToolsEnhancer({ name: '0x Website Redux Store' }),
+);
+store.subscribe(
+ _.throttle(() => {
+ // Persisted state
+ stateStorage.saveState({
+ hasPortalOnboardingBeenSeen: store.getState().hasPortalOnboardingBeenSeen,
+ });
+ }, ONE_SECOND),
+);
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/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,
};
diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts
index e79a873e0..b9d962b75 100644
--- a/packages/website/ts/utils/utils.ts
+++ b/packages/website/ts/utils/utils.ts
@@ -11,6 +11,7 @@ import {
Environments,
Order,
Providers,
+ ProviderType,
ScreenWidths,
Side,
SideToAssetToken,
@@ -313,8 +314,16 @@ 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();
},
+ getEthToken(tokenByAddress: TokenByAddress): Token {
+ const tokens = _.values(tokenByAddress);
+ const etherToken = _.find(tokens, { symbol: constants.ETHER_TOKEN_SYMBOL });
+ return etherToken;
+ },
};