Merge pull request #811 from 0xProject/feature/website/onboarding-polish-2
Add Hotjar, and other small improvements
This commit is contained in:
@@ -70,7 +70,18 @@
|
||||
})(document, 'script', 'twitter-wjs');
|
||||
</script>
|
||||
<!-- End Twitter SDK -->
|
||||
|
||||
<!-- Hotjar Tracking Code for https://0xproject.com/ -->
|
||||
<script>
|
||||
(function (h, o, t, j, a, r) {
|
||||
h.hj = h.hj || function () { (h.hj.q = h.hj.q || []).push(arguments) };
|
||||
h._hjSettings = { hjid: 935597, hjsv: 6 };
|
||||
a = o.getElementsByTagName('head')[0];
|
||||
r = o.createElement('script'); r.async = 1;
|
||||
r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;
|
||||
a.appendChild(r);
|
||||
})(window, document, 'https://static.hotjar.com/c/hotjar-', '.js?sv=');
|
||||
</script>
|
||||
<!-- End Hotjar Tracking Code -->
|
||||
<!-- Main -->
|
||||
<script type="text/javascript" crossorigin="anonymous" src="/bundle.js" charset="utf-8"></script>
|
||||
</body>
|
||||
|
||||
@@ -39,7 +39,7 @@ export const OnboardingCard: React.StatelessComponent<OnboardingCardProps> = ({
|
||||
borderRadius,
|
||||
}) => (
|
||||
<Island borderRadius={borderRadius}>
|
||||
<Container paddingRight="30px" paddingLeft="30px" maxWidth={350} paddingTop="15px" paddingBottom="15px">
|
||||
<Container paddingRight="30px" paddingLeft="30px" paddingTop="15px" paddingBottom="15px">
|
||||
<div className="flex flex-column">
|
||||
<div className="flex justify-between">
|
||||
<Title>{title}</Title>
|
||||
|
||||
@@ -6,13 +6,29 @@ import { ContinueButtonDisplay, OnboardingTooltip } from 'ts/components/onboardi
|
||||
import { Animation } from 'ts/components/ui/animation';
|
||||
import { Container } from 'ts/components/ui/container';
|
||||
import { Overlay } from 'ts/components/ui/overlay';
|
||||
import { PointerDirection } from 'ts/components/ui/pointer';
|
||||
import { zIndex } from 'ts/style/z_index';
|
||||
|
||||
export interface Step {
|
||||
export interface FixedPositionSettings {
|
||||
type: 'fixed';
|
||||
top?: string;
|
||||
bottom?: string;
|
||||
left?: string;
|
||||
right?: string;
|
||||
pointerDirection?: PointerDirection;
|
||||
}
|
||||
|
||||
export interface TargetPositionSettings {
|
||||
type: 'target';
|
||||
target: string;
|
||||
placement: Placement;
|
||||
}
|
||||
|
||||
export interface Step {
|
||||
// Provide either a CSS selector, or fixed position settings. Only applies to desktop.
|
||||
position: TargetPositionSettings | FixedPositionSettings;
|
||||
title?: string;
|
||||
content: React.ReactNode;
|
||||
placement?: Placement;
|
||||
shouldHideBackButton?: boolean;
|
||||
shouldHideNextButton?: boolean;
|
||||
continueButtonDisplay?: ContinueButtonDisplay;
|
||||
@@ -40,18 +56,30 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
|
||||
return null;
|
||||
}
|
||||
let onboardingElement = null;
|
||||
const currentStep = this._getCurrentStep();
|
||||
if (this.props.isMobile) {
|
||||
onboardingElement = <Animation type="easeUpFromBottom">{this._renderOnboardignCard()}</Animation>;
|
||||
} else {
|
||||
onboardingElement = <Animation type="easeUpFromBottom">{this._renderOnboardingCard()}</Animation>;
|
||||
} else if (currentStep.position.type === 'target') {
|
||||
const { placement, target } = currentStep.position;
|
||||
onboardingElement = (
|
||||
<Popper
|
||||
referenceElement={this._getElementForStep()}
|
||||
placement={this._getCurrentStep().placement}
|
||||
positionFixed={true}
|
||||
>
|
||||
<Popper referenceElement={document.querySelector(target)} placement={placement} positionFixed={true}>
|
||||
{this._renderPopperChildren.bind(this)}
|
||||
</Popper>
|
||||
);
|
||||
} else if (currentStep.position.type === 'fixed') {
|
||||
const { top, right, bottom, left, pointerDirection } = currentStep.position;
|
||||
onboardingElement = (
|
||||
<Container
|
||||
position="fixed"
|
||||
zIndex={zIndex.aboveOverlay}
|
||||
top={top}
|
||||
right={right}
|
||||
bottom={bottom}
|
||||
left={left}
|
||||
>
|
||||
{this._renderToolTip(pointerDirection)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
if (this.props.disableOverlay) {
|
||||
return onboardingElement;
|
||||
@@ -63,9 +91,6 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
private _getElementForStep(): Element {
|
||||
return document.querySelector(this._getCurrentStep().target);
|
||||
}
|
||||
private _renderPopperChildren(props: PopperChildrenProps): React.ReactNode {
|
||||
const customStyles = { zIndex: zIndex.aboveOverlay };
|
||||
// On re-render, we want to re-center the popper.
|
||||
@@ -76,7 +101,7 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
private _renderToolTip(): React.ReactNode {
|
||||
private _renderToolTip(pointerDirection?: PointerDirection): React.ReactNode {
|
||||
const { steps, stepIndex } = this.props;
|
||||
const step = steps[stepIndex];
|
||||
const isLastStep = steps.length - 1 === stepIndex;
|
||||
@@ -94,12 +119,13 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
|
||||
continueButtonDisplay={step.continueButtonDisplay}
|
||||
continueButtonText={step.continueButtonText}
|
||||
onContinueButtonClick={step.onContinueButtonClick}
|
||||
pointerDirection={pointerDirection}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
private _renderOnboardignCard(): React.ReactNode {
|
||||
private _renderOnboardingCard(): React.ReactNode {
|
||||
const { steps, stepIndex } = this.props;
|
||||
const step = steps[stepIndex];
|
||||
const isLastStep = steps.length - 1 === stepIndex;
|
||||
|
||||
@@ -9,7 +9,12 @@ import { AddEthOnboardingStep } from 'ts/components/onboarding/add_eth_onboardin
|
||||
import { CongratsOnboardingStep } from 'ts/components/onboarding/congrats_onboarding_step';
|
||||
import { InstallWalletOnboardingStep } from 'ts/components/onboarding/install_wallet_onboarding_step';
|
||||
import { IntroOnboardingStep } from 'ts/components/onboarding/intro_onboarding_step';
|
||||
import { OnboardingFlow, Step } from 'ts/components/onboarding/onboarding_flow';
|
||||
import {
|
||||
FixedPositionSettings,
|
||||
OnboardingFlow,
|
||||
Step,
|
||||
TargetPositionSettings,
|
||||
} from 'ts/components/onboarding/onboarding_flow';
|
||||
import { SetAllowancesOnboardingStep } from 'ts/components/onboarding/set_allowances_onboarding_step';
|
||||
import { UnlockWalletOnboardingStep } from 'ts/components/onboarding/unlock_wallet_onboarding_step';
|
||||
import {
|
||||
@@ -45,8 +50,6 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
|
||||
private _unlisten: () => void;
|
||||
public componentDidMount(): void {
|
||||
this._adjustStepIfShould();
|
||||
// Wait until the step is adjusted to decide whether we should show onboarding.
|
||||
setTimeout(this._autoStartOnboardingIfShould.bind(this), 1000);
|
||||
// If there is a route change, just close onboarding.
|
||||
this._unlisten = this.props.history.listen(() => this.props.updateIsRunning(false));
|
||||
}
|
||||
@@ -61,6 +64,9 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
|
||||
document.querySelector('.wallet').scrollIntoView();
|
||||
}
|
||||
}
|
||||
if (!prevProps.blockchainIsLoaded && this.props.blockchainIsLoaded) {
|
||||
this._autoStartOnboardingIfShould();
|
||||
}
|
||||
}
|
||||
public render(): React.ReactNode {
|
||||
return (
|
||||
@@ -76,56 +82,61 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
|
||||
);
|
||||
}
|
||||
private _getSteps(): Step[] {
|
||||
const nextToWalletPosition: TargetPositionSettings = {
|
||||
type: 'target',
|
||||
target: '.wallet',
|
||||
placement: 'right',
|
||||
};
|
||||
const underMetamaskExtension: FixedPositionSettings = {
|
||||
type: 'fixed',
|
||||
top: '30px',
|
||||
right: '10px',
|
||||
pointerDirection: 'top',
|
||||
};
|
||||
const steps: Step[] = [
|
||||
{
|
||||
target: '.wallet',
|
||||
position: nextToWalletPosition,
|
||||
title: '0x Ecosystem Setup',
|
||||
content: <InstallWalletOnboardingStep />,
|
||||
placement: 'right',
|
||||
shouldHideBackButton: true,
|
||||
shouldHideNextButton: true,
|
||||
},
|
||||
{
|
||||
target: '.wallet',
|
||||
position: underMetamaskExtension,
|
||||
title: '0x Ecosystem Setup',
|
||||
content: <UnlockWalletOnboardingStep />,
|
||||
placement: 'right',
|
||||
shouldHideBackButton: true,
|
||||
shouldHideNextButton: true,
|
||||
},
|
||||
{
|
||||
target: '.wallet',
|
||||
position: nextToWalletPosition,
|
||||
title: '0x Ecosystem Account Setup',
|
||||
content: <IntroOnboardingStep />,
|
||||
placement: 'right',
|
||||
shouldHideBackButton: true,
|
||||
continueButtonDisplay: 'enabled',
|
||||
},
|
||||
{
|
||||
target: '.wallet',
|
||||
position: nextToWalletPosition,
|
||||
title: 'Step 1: Add ETH',
|
||||
content: (
|
||||
<AddEthOnboardingStep userEthBalanceInWei={this.props.userEtherBalanceInWei || new BigNumber(0)} />
|
||||
),
|
||||
placement: 'right',
|
||||
continueButtonDisplay: this._userHasVisibleEth() ? 'enabled' : 'disabled',
|
||||
},
|
||||
{
|
||||
target: '.wallet',
|
||||
position: nextToWalletPosition,
|
||||
title: 'Step 2: Wrap ETH',
|
||||
content: <WrapEthOnboardingStep1 />,
|
||||
placement: 'right',
|
||||
continueButtonDisplay: 'enabled',
|
||||
},
|
||||
{
|
||||
target: '.wallet',
|
||||
position: nextToWalletPosition,
|
||||
title: 'Step 2: Wrap ETH',
|
||||
content: <WrapEthOnboardingStep2 />,
|
||||
placement: 'right',
|
||||
continueButtonDisplay: this._userHasVisibleWeth() ? 'enabled' : 'disabled',
|
||||
},
|
||||
{
|
||||
target: '.wallet',
|
||||
position: nextToWalletPosition,
|
||||
title: 'Step 2: Wrap ETH',
|
||||
content: (
|
||||
<WrapEthOnboardingStep3
|
||||
@@ -134,11 +145,10 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
|
||||
}
|
||||
/>
|
||||
),
|
||||
placement: 'right',
|
||||
continueButtonDisplay: this._userHasVisibleWeth() ? 'enabled' : 'disabled',
|
||||
},
|
||||
{
|
||||
target: '.wallet',
|
||||
position: nextToWalletPosition,
|
||||
title: 'Step 3: Unlock Tokens',
|
||||
content: (
|
||||
<SetAllowancesOnboardingStep
|
||||
@@ -147,14 +157,12 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
|
||||
doesUserHaveAllowancesForWethAndZrx={this._doesUserHaveAllowancesForWethAndZrx()}
|
||||
/>
|
||||
),
|
||||
placement: 'right',
|
||||
continueButtonDisplay: this._doesUserHaveAllowancesForWethAndZrx() ? 'enabled' : 'disabled',
|
||||
},
|
||||
{
|
||||
target: '.wallet',
|
||||
position: nextToWalletPosition,
|
||||
title: '🎉 The Ecosystem Awaits',
|
||||
content: <CongratsOnboardingStep />,
|
||||
placement: 'right',
|
||||
continueButtonDisplay: 'enabled',
|
||||
shouldHideNextButton: true,
|
||||
continueButtonText: 'Enter the 0x Ecosystem',
|
||||
@@ -221,7 +229,7 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
|
||||
}
|
||||
private _autoStartOnboardingIfShould(): void {
|
||||
if (
|
||||
(this.props.stepIndex === 0 && !this.props.isRunning) ||
|
||||
(this.props.stepIndex === 0 && !this.props.isRunning && this.props.blockchainIsLoaded) ||
|
||||
(!this.props.isRunning && !this.props.hasBeenClosed && this.props.blockchainIsLoaded)
|
||||
) {
|
||||
const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
|
||||
|
||||
@@ -10,7 +10,7 @@ export const UnlockWalletOnboardingStep: React.StatelessComponent<UnlockWalletOn
|
||||
<Container marginTop="15px" marginBottom="15px">
|
||||
<img src="/images/metamask_icon.png" height="50px" width="50px" />
|
||||
</Container>
|
||||
<Text center={true}>Unlock your metamask extension to get started.</Text>
|
||||
<Text center={true}>Unlock your MetaMask extension to get started.</Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Container } from 'ts/components/ui/container';
|
||||
import { Image } from 'ts/components/ui/image';
|
||||
import { Island } from 'ts/components/ui/island';
|
||||
import { colors } from 'ts/style/colors';
|
||||
import { media } from 'ts/style/media';
|
||||
import { styled } from 'ts/style/theme';
|
||||
import { WebsiteBackendRelayerInfo } from 'ts/types';
|
||||
import { utils } from 'ts/utils/utils';
|
||||
@@ -111,6 +112,9 @@ const GridTile = styled(PlainGridTile)`
|
||||
&:hover {
|
||||
transform: translate(0px, -3px);
|
||||
}
|
||||
${media.small`
|
||||
transform: none;
|
||||
`};
|
||||
`;
|
||||
|
||||
interface SectionProps {
|
||||
|
||||
@@ -13,9 +13,11 @@ export const store: ReduxStore<State> = createStore(
|
||||
);
|
||||
store.subscribe(
|
||||
_.throttle(() => {
|
||||
const state = store.getState();
|
||||
// Persisted state
|
||||
stateStorage.saveState({
|
||||
hasPortalOnboardingBeenClosed: store.getState().hasPortalOnboardingBeenClosed,
|
||||
hasPortalOnboardingBeenClosed: state.hasPortalOnboardingBeenClosed,
|
||||
isPortalOnboardingShowing: state.isPortalOnboardingShowing,
|
||||
});
|
||||
}, ONE_SECOND),
|
||||
);
|
||||
|
||||
14
packages/website/ts/style/media.ts
Normal file
14
packages/website/ts/style/media.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { css } from 'ts/style/theme';
|
||||
import { ScreenWidths } from 'ts/types';
|
||||
|
||||
const generateMediaWrapper = (screenWidth: ScreenWidths) => (...args: any[]) => css`
|
||||
@media (max-width: ${screenWidth}) {
|
||||
${css.apply(css, args)};
|
||||
}
|
||||
`;
|
||||
|
||||
export const media = {
|
||||
small: generateMediaWrapper(ScreenWidths.Sm),
|
||||
medium: generateMediaWrapper(ScreenWidths.Md),
|
||||
large: generateMediaWrapper(ScreenWidths.Lg),
|
||||
};
|
||||
@@ -215,10 +215,11 @@ export interface ContractEvent {
|
||||
}
|
||||
|
||||
export type ValidatedBigNumberCallback = (isValid: boolean, amount?: BigNumber) => void;
|
||||
// Associated values are in `em` units
|
||||
export enum ScreenWidths {
|
||||
Sm = 'SM',
|
||||
Md = 'MD',
|
||||
Lg = 'LG',
|
||||
Sm = 40,
|
||||
Md = 52,
|
||||
Lg = 64,
|
||||
}
|
||||
|
||||
export enum AlertTypes {
|
||||
|
||||
@@ -29,9 +29,6 @@ import { configs } from 'ts/utils/configs';
|
||||
import { constants } from 'ts/utils/constants';
|
||||
import * as u2f from 'ts/vendor/u2f_api';
|
||||
|
||||
const LG_MIN_EM = 64;
|
||||
const MD_MIN_EM = 52;
|
||||
|
||||
const isDogfood = (): boolean => _.includes(window.location.href, configs.DOMAIN_DOGFOOD);
|
||||
|
||||
export const utils = {
|
||||
@@ -136,9 +133,9 @@ export const utils = {
|
||||
|
||||
// This logic mirrors the CSS media queries in BassCSS for the `lg-`, `md-` and `sm-` CSS
|
||||
// class prefixes. Do not edit these.
|
||||
if (widthInEm > LG_MIN_EM) {
|
||||
if (widthInEm > ScreenWidths.Lg) {
|
||||
return ScreenWidths.Lg;
|
||||
} else if (widthInEm > MD_MIN_EM) {
|
||||
} else if (widthInEm > ScreenWidths.Md) {
|
||||
return ScreenWidths.Md;
|
||||
} else {
|
||||
return ScreenWidths.Sm;
|
||||
|
||||
Reference in New Issue
Block a user