aboutsummaryrefslogtreecommitdiffstats
path: root/packages/instant
diff options
context:
space:
mode:
authorfragosti <francesco.agosti93@gmail.com>2018-12-01 03:58:05 +0800
committerfragosti <francesco.agosti93@gmail.com>2018-12-01 03:58:05 +0800
commitea11e8c62dac9ff6352bc983efc707d855b8e881 (patch)
treecdfc9ad01c2a4e56c560809cb7e01895987ef442 /packages/instant
parent7479a7db58f5504416a88450c92b092899bf3df3 (diff)
parenta1d4aa66bc6b3de041ec6e4eb4fe40383945510b (diff)
downloaddexon-0x-contracts-ea11e8c62dac9ff6352bc983efc707d855b8e881.tar
dexon-0x-contracts-ea11e8c62dac9ff6352bc983efc707d855b8e881.tar.gz
dexon-0x-contracts-ea11e8c62dac9ff6352bc983efc707d855b8e881.tar.bz2
dexon-0x-contracts-ea11e8c62dac9ff6352bc983efc707d855b8e881.tar.lz
dexon-0x-contracts-ea11e8c62dac9ff6352bc983efc707d855b8e881.tar.xz
dexon-0x-contracts-ea11e8c62dac9ff6352bc983efc707d855b8e881.tar.zst
dexon-0x-contracts-ea11e8c62dac9ff6352bc983efc707d855b8e881.zip
Merge branch 'development' of https://github.com/0xProject/0x-monorepo into fix/instant/instant-bounces-on-error
Diffstat (limited to 'packages/instant')
-rw-r--r--packages/instant/src/components/erc20_token_selector.tsx8
-rw-r--r--packages/instant/src/components/install_wallet_panel_content.tsx9
-rw-r--r--packages/instant/src/components/scaling_input.tsx6
-rw-r--r--packages/instant/src/components/standard_panel_content.tsx13
-rw-r--r--packages/instant/src/components/timed_progress_bar.tsx17
-rw-r--r--packages/instant/src/components/ui/container.tsx13
-rw-r--r--packages/instant/src/components/ui/text.tsx6
-rw-r--r--packages/instant/src/components/zero_ex_instant_provider.tsx6
-rw-r--r--packages/instant/src/containers/connected_account_payment_method.ts37
-rw-r--r--packages/instant/src/containers/selected_erc20_asset_amount_input.ts4
-rw-r--r--packages/instant/src/redux/analytics_middleware.ts14
-rw-r--r--packages/instant/src/redux/async_data.ts10
-rw-r--r--packages/instant/src/style/theme.ts12
-rw-r--r--packages/instant/src/types.ts10
-rw-r--r--packages/instant/src/util/analytics.ts38
-rw-r--r--packages/instant/src/util/buy_quote_updater.ts12
-rw-r--r--packages/instant/src/util/heartbeater_factory.ts12
17 files changed, 181 insertions, 46 deletions
diff --git a/packages/instant/src/components/erc20_token_selector.tsx b/packages/instant/src/components/erc20_token_selector.tsx
index 0a3d4427a..f7d5a4fe4 100644
--- a/packages/instant/src/components/erc20_token_selector.tsx
+++ b/packages/instant/src/components/erc20_token_selector.tsx
@@ -19,12 +19,12 @@ export interface ERC20TokenSelectorProps {
}
export interface ERC20TokenSelectorState {
- searchQuery?: string;
+ searchQuery: string;
}
export class ERC20TokenSelector extends React.Component<ERC20TokenSelectorProps> {
public state: ERC20TokenSelectorState = {
- searchQuery: undefined,
+ searchQuery: '',
};
public render(): React.ReactNode {
const { tokens, onTokenSelect } = this.props;
@@ -62,10 +62,10 @@ export class ERC20TokenSelector extends React.Component<ERC20TokenSelectorProps>
};
private readonly _isTokenQueryMatch = (token: ERC20Asset): boolean => {
const { searchQuery } = this.state;
- if (_.isUndefined(searchQuery)) {
+ const searchQueryLowerCase = searchQuery.toLowerCase().trim();
+ if (searchQueryLowerCase === '') {
return true;
}
- const searchQueryLowerCase = searchQuery.toLowerCase();
const tokenName = token.metaData.name.toLowerCase();
const tokenSymbol = token.metaData.symbol.toLowerCase();
return _.startsWith(tokenSymbol, searchQueryLowerCase) || _.startsWith(tokenName, searchQueryLowerCase);
diff --git a/packages/instant/src/components/install_wallet_panel_content.tsx b/packages/instant/src/components/install_wallet_panel_content.tsx
index 88c26f59c..481d82da0 100644
--- a/packages/instant/src/components/install_wallet_panel_content.tsx
+++ b/packages/instant/src/components/install_wallet_panel_content.tsx
@@ -8,7 +8,9 @@ import {
} from '../constants';
import { ColorOption } from '../style/theme';
import { Browser } from '../types';
+import { analytics } from '../util/analytics';
import { envUtil } from '../util/env';
+import { util } from '../util/util';
import { MetaMaskLogo } from './meta_mask_logo';
import { StandardPanelContent, StandardPanelContentProps } from './standard_panel_content';
@@ -45,6 +47,10 @@ export class InstallWalletPanelContent extends React.Component<InstallWalletPane
default:
break;
}
+ const onActionClick = () => {
+ analytics.trackInstallWalletModalClickedGet();
+ util.createOpenUrlInNewWindow(actionUrl)();
+ };
return {
image: <MetaMaskLogo width={85} height={80} />,
title: 'Install MetaMask',
@@ -52,10 +58,11 @@ export class InstallWalletPanelContent extends React.Component<InstallWalletPane
moreInfoSettings: {
href: META_MASK_SITE_URL,
text: 'What is MetaMask?',
+ onClick: analytics.trackInstallWalletModalClickedExplanation,
},
action: (
<Button
- href={actionUrl}
+ onClick={onActionClick}
width="100%"
fontColor={ColorOption.white}
backgroundColor={ColorOption.darkOrange}
diff --git a/packages/instant/src/components/scaling_input.tsx b/packages/instant/src/components/scaling_input.tsx
index 129162a74..791692257 100644
--- a/packages/instant/src/components/scaling_input.tsx
+++ b/packages/instant/src/components/scaling_input.tsx
@@ -98,6 +98,12 @@ export class ScalingInput extends React.Component<ScalingInputProps, ScalingInpu
inputWidthPx: this._getInputWidthInPx(),
};
}
+ public componentDidMount(): void {
+ // Trigger an initial notification of the calculated fontSize.
+ const currentPhase = ScalingInput.getPhaseFromProps(this.props);
+ const currentFontSize = ScalingInput.calculateFontSizeFromProps(this.props, currentPhase);
+ this.props.onFontSizeChange(currentFontSize);
+ }
public componentDidUpdate(
prevProps: ScalingInputProps,
prevState: ScalingInputState,
diff --git a/packages/instant/src/components/standard_panel_content.tsx b/packages/instant/src/components/standard_panel_content.tsx
index 582b3318e..79b7bff24 100644
--- a/packages/instant/src/components/standard_panel_content.tsx
+++ b/packages/instant/src/components/standard_panel_content.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import { ColorOption } from '../style/theme';
+import { util } from '../util/util';
import { Container } from './ui/container';
import { Flex } from './ui/flex';
@@ -9,6 +10,7 @@ import { Text } from './ui/text';
export interface MoreInfoSettings {
text: string;
href: string;
+ onClick?: () => void;
}
export interface StandardPanelContentProps {
@@ -21,6 +23,15 @@ export interface StandardPanelContentProps {
const SPACING_BETWEEN_PX = '20px';
+const onMoreInfoClick = (href: string, onClick?: () => void) => {
+ return () => {
+ if (onClick) {
+ onClick();
+ }
+ util.createOpenUrlInNewWindow(href)();
+ };
+};
+
export const StandardPanelContent: React.StatelessComponent<StandardPanelContentProps> = ({
image,
title,
@@ -50,7 +61,7 @@ export const StandardPanelContent: React.StatelessComponent<StandardPanelContent
fontSize="13px"
textDecorationLine="underline"
fontColor={ColorOption.lightGrey}
- href={moreInfoSettings.href}
+ onClick={onMoreInfoClick(moreInfoSettings.href, moreInfoSettings.onClick)}
>
{moreInfoSettings.text}
</Text>
diff --git a/packages/instant/src/components/timed_progress_bar.tsx b/packages/instant/src/components/timed_progress_bar.tsx
index 8465b9cd0..fb3927088 100644
--- a/packages/instant/src/components/timed_progress_bar.tsx
+++ b/packages/instant/src/components/timed_progress_bar.tsx
@@ -1,8 +1,9 @@
import * as _ from 'lodash';
+import { transparentize } from 'polished';
import * as React from 'react';
import { PROGRESS_FINISH_ANIMATION_TIME_MS, PROGRESS_STALL_AT_WIDTH } from '../constants';
-import { ColorOption, css, keyframes, styled } from '../style/theme';
+import { ColorOption, css, keyframes, styled, ThemeConsumer } from '../style/theme';
import { Container } from './ui/container';
@@ -93,8 +94,16 @@ export interface ProgressBarProps extends ProgressProps {}
export const ProgressBar: React.ComponentType<ProgressBarProps & React.ClassAttributes<{}>> = React.forwardRef(
(props, ref) => (
- <Container width="100%" backgroundColor={ColorOption.lightGrey} borderRadius="6px">
- <Progress {...props} ref={ref as any} />
- </Container>
+ <ThemeConsumer>
+ {theme => (
+ <Container
+ width="100%"
+ borderRadius="6px"
+ rawBackgroundColor={transparentize(0.5, theme[ColorOption.primaryColor])}
+ >
+ <Progress {...props} ref={ref as any} />
+ </Container>
+ )}
+ </ThemeConsumer>
),
);
diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx
index 2143b0503..e7d909d92 100644
--- a/packages/instant/src/components/ui/container.tsx
+++ b/packages/instant/src/components/ui/container.tsx
@@ -27,6 +27,7 @@ export interface ContainerProps {
borderBottom?: string;
className?: string;
backgroundColor?: ColorOption;
+ rawBackgroundColor?: string;
hasBoxShadow?: boolean;
isHidden?: boolean;
zIndex?: number;
@@ -39,6 +40,16 @@ export interface ContainerProps {
flexGrow?: string | number;
}
+const getBackgroundColor = (theme: any, backgroundColor?: ColorOption, rawBackgroundColor?: string): string => {
+ if (backgroundColor) {
+ return theme[backgroundColor] as string;
+ }
+ if (rawBackgroundColor) {
+ return rawBackgroundColor;
+ }
+ return 'none';
+};
+
export const Container =
styled.div <
ContainerProps >
@@ -72,7 +83,7 @@ export const Container =
${props => props.height && stylesForMedia<string>('height', props.height)}
${props => props.borderRadius && stylesForMedia<string>('border-radius', props.borderRadius)}
${props => (props.isHidden ? 'visibility: hidden;' : '')}
- background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
+ background-color: ${props => getBackgroundColor(props.theme, props.backgroundColor, props.rawBackgroundColor)};
border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')};
&:hover {
${props =>
diff --git a/packages/instant/src/components/ui/text.tsx b/packages/instant/src/components/ui/text.tsx
index 8e573d7b9..282477758 100644
--- a/packages/instant/src/components/ui/text.tsx
+++ b/packages/instant/src/components/ui/text.tsx
@@ -1,4 +1,3 @@
-import { darken } from 'polished';
import * as React from 'react';
import { ColorOption, styled } from '../../style/theme';
@@ -31,7 +30,7 @@ export const Text: React.StatelessComponent<TextProps> = ({ href, onClick, ...re
return <StyledText {...rest} onClick={computedOnClick} />;
};
-const darkenOnHoverAmount = 0.3;
+const opacityOnHoverAmount = 0.5;
export const StyledText =
styled.div <
TextProps >
@@ -56,8 +55,7 @@ export const StyledText =
${props => (props.textAlign ? `text-align: ${props.textAlign}` : '')};
${props => (props.width ? `width: ${props.width}` : '')};
&:hover {
- ${props =>
- props.onClick ? `color: ${darken(darkenOnHoverAmount, props.theme[props.fontColor || 'white'])}` : ''};
+ ${props => (props.onClick ? `opacity: ${opacityOnHoverAmount};` : '')};
}
}
`;
diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx
index e006a5a5f..a4a03bbf4 100644
--- a/packages/instant/src/components/zero_ex_instant_provider.tsx
+++ b/packages/instant/src/components/zero_ex_instant_provider.tsx
@@ -11,7 +11,7 @@ import { asyncData } from '../redux/async_data';
import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer';
import { store, Store } from '../redux/store';
import { fonts } from '../style/fonts';
-import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types';
+import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource, QuoteFetchOrigin } from '../types';
import { analytics, disableAnalytics } from '../util/analytics';
import { assetUtils } from '../util/asset';
import { errorFlasher } from '../util/error_flasher';
@@ -115,7 +115,9 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
this._buyQuoteHeartbeat.start(BUY_QUOTE_UPDATE_INTERVAL_TIME_MS);
// Trigger first buyquote fetch
// tslint:disable-next-line:no-floating-promises
- asyncData.fetchCurrentBuyQuoteAndDispatchToStore(state, dispatch, { updateSilently: false });
+ asyncData.fetchCurrentBuyQuoteAndDispatchToStore(state, dispatch, QuoteFetchOrigin.Manual, {
+ updateSilently: false,
+ });
// warm up the gas price estimator cache just in case we can't
// grab the gas price estimate when submitting the transaction
// tslint:disable-next-line:no-floating-promises
diff --git a/packages/instant/src/containers/connected_account_payment_method.ts b/packages/instant/src/containers/connected_account_payment_method.ts
index cdeb49a25..e9327a288 100644
--- a/packages/instant/src/containers/connected_account_payment_method.ts
+++ b/packages/instant/src/containers/connected_account_payment_method.ts
@@ -11,7 +11,7 @@ import {
import { Action, actions } from '../redux/actions';
import { asyncData } from '../redux/async_data';
import { State } from '../redux/reducer';
-import { Network, Omit, OperatingSystem, ProviderState, StandardSlidingPanelContent } from '../types';
+import { Network, Omit, OperatingSystem, ProviderState, StandardSlidingPanelContent, WalletSuggestion } from '../types';
import { analytics } from '../util/analytics';
import { envUtil } from '../util/env';
@@ -60,23 +60,28 @@ const mergeProps = (
onUnlockWalletClick: () => connectedDispatch.unlockWalletAndDispatchToStore(connectedState.providerState),
onInstallWalletClick: () => {
const isMobile = envUtil.isMobileOperatingSystem();
- if (!isMobile) {
+ const walletSuggestion: WalletSuggestion = isMobile
+ ? WalletSuggestion.CoinbaseWallet
+ : WalletSuggestion.MetaMask;
+
+ analytics.trackInstallWalletClicked(walletSuggestion);
+ if (walletSuggestion === WalletSuggestion.MetaMask) {
connectedDispatch.openInstallWalletPanel();
- return;
- }
- const operatingSystem = envUtil.getOperatingSystem();
- let url = COINBASE_WALLET_SITE_URL;
- switch (operatingSystem) {
- case OperatingSystem.Android:
- url = COINBASE_WALLET_ANDROID_APP_STORE_URL;
- break;
- case OperatingSystem.iOS:
- url = COINBASE_WALLET_IOS_APP_STORE_URL;
- break;
- default:
- break;
+ } else {
+ const operatingSystem = envUtil.getOperatingSystem();
+ let url = COINBASE_WALLET_SITE_URL;
+ switch (operatingSystem) {
+ case OperatingSystem.Android:
+ url = COINBASE_WALLET_ANDROID_APP_STORE_URL;
+ break;
+ case OperatingSystem.iOS:
+ url = COINBASE_WALLET_IOS_APP_STORE_URL;
+ break;
+ default:
+ break;
+ }
+ window.open(url, '_blank');
}
- window.open(url, '_blank');
},
});
diff --git a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
index a39bc46a2..cb9df527e 100644
--- a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
+++ b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
@@ -10,7 +10,7 @@ import { ERC20AssetAmountInput, ERC20AssetAmountInputProps } from '../components
import { Action, actions } from '../redux/actions';
import { State } from '../redux/reducer';
import { ColorOption } from '../style/theme';
-import { AffiliateInfo, ERC20Asset, Omit, OrderProcessState } from '../types';
+import { AffiliateInfo, ERC20Asset, Omit, OrderProcessState, QuoteFetchOrigin } from '../types';
import { buyQuoteUpdater } from '../util/buy_quote_updater';
export interface SelectedERC20AssetAmountInputProps {
@@ -88,7 +88,7 @@ const mapDispatchToProps = (
// even if it's debounced, give them the illusion it's loading
dispatch(actions.setQuoteRequestStatePending());
// tslint:disable-next-line:no-floating-promises
- debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value, {
+ debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value, QuoteFetchOrigin.Manual, {
setPending: true,
dispatchErrors: true,
affiliateInfo,
diff --git a/packages/instant/src/redux/analytics_middleware.ts b/packages/instant/src/redux/analytics_middleware.ts
index 8aa76eb77..3dc5fe924 100644
--- a/packages/instant/src/redux/analytics_middleware.ts
+++ b/packages/instant/src/redux/analytics_middleware.ts
@@ -3,7 +3,7 @@ import * as _ from 'lodash';
import { Middleware } from 'redux';
import { ETH_DECIMALS } from '../constants';
-import { Account, AccountState } from '../types';
+import { Account, AccountState, StandardSlidingPanelContent } from '../types';
import { analytics } from '../util/analytics';
import { Action, ActionTypes } from './actions';
@@ -77,6 +77,18 @@ export const analyticsMiddleware: Middleware = store => next => middlewareAction
});
}
break;
+ case ActionTypes.OPEN_STANDARD_SLIDING_PANEL:
+ const openSlidingContent = curState.standardSlidingPanelSettings.content;
+ if (openSlidingContent === StandardSlidingPanelContent.InstallWallet) {
+ analytics.trackInstallWalletModalOpened();
+ }
+ break;
+ case ActionTypes.CLOSE_STANDARD_SLIDING_PANEL:
+ const closeSlidingContent = curState.standardSlidingPanelSettings.content;
+ if (closeSlidingContent === StandardSlidingPanelContent.InstallWallet) {
+ analytics.trackInstallWalletModalClosed();
+ }
+ break;
}
return nextAction;
diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts
index 6feb760e7..9fdcea3ca 100644
--- a/packages/instant/src/redux/async_data.ts
+++ b/packages/instant/src/redux/async_data.ts
@@ -4,7 +4,7 @@ import * as _ from 'lodash';
import { Dispatch } from 'redux';
import { BIG_NUMBER_ZERO } from '../constants';
-import { AccountState, ERC20Asset, OrderProcessState, ProviderState } from '../types';
+import { AccountState, ERC20Asset, OrderProcessState, ProviderState, QuoteFetchOrigin } from '../types';
import { analytics } from '../util/analytics';
import { assetUtils } from '../util/asset';
import { buyQuoteUpdater } from '../util/buy_quote_updater';
@@ -84,6 +84,7 @@ export const asyncData = {
fetchCurrentBuyQuoteAndDispatchToStore: async (
state: State,
dispatch: Dispatch,
+ fetchOrigin: QuoteFetchOrigin,
options: { updateSilently: boolean },
) => {
const { buyOrderState, providerState, selectedAsset, selectedAssetUnitAmount, affiliateInfo } = state;
@@ -99,7 +100,12 @@ export const asyncData = {
dispatch,
selectedAsset as ERC20Asset,
selectedAssetUnitAmount,
- { setPending: !options.updateSilently, dispatchErrors: !options.updateSilently, affiliateInfo },
+ fetchOrigin,
+ {
+ setPending: !options.updateSilently,
+ dispatchErrors: !options.updateSilently,
+ affiliateInfo,
+ },
);
}
},
diff --git a/packages/instant/src/style/theme.ts b/packages/instant/src/style/theme.ts
index a0751286b..71e4b7052 100644
--- a/packages/instant/src/style/theme.ts
+++ b/packages/instant/src/style/theme.ts
@@ -1,6 +1,14 @@
import * as styledComponents from 'styled-components';
-const { default: styled, css, keyframes, withTheme, createGlobalStyle, ThemeProvider } = styledComponents;
+const {
+ default: styled,
+ css,
+ keyframes,
+ withTheme,
+ createGlobalStyle,
+ ThemeConsumer,
+ ThemeProvider,
+} = styledComponents;
export type Theme = { [key in ColorOption]: string };
@@ -45,4 +53,4 @@ export const generateOverlayBlack = (opacity = 0.6) => {
return `rgba(0, 0, 0, ${opacity})`;
};
-export { styled, css, keyframes, withTheme, createGlobalStyle, ThemeProvider };
+export { styled, css, keyframes, withTheme, createGlobalStyle, ThemeConsumer, ThemeProvider };
diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts
index 999d50fed..2d73ba29e 100644
--- a/packages/instant/src/types.ts
+++ b/packages/instant/src/types.ts
@@ -21,6 +21,11 @@ export enum OrderProcessState {
Failure = 'FAILURE',
}
+export enum QuoteFetchOrigin {
+ Manual = 'Manual',
+ Heartbeat = 'Heartbeat',
+}
+
export interface SimulatedProgress {
startTimeUnix: number;
expectedEndTimeUnix: number;
@@ -149,6 +154,11 @@ export enum Browser {
Other = 'OTHER',
}
+export enum WalletSuggestion {
+ CoinbaseWallet = 'Coinbase Wallet',
+ MetaMask = 'MetaMask',
+}
+
export enum OperatingSystem {
Android = 'ANDROID',
iOS = 'IOS',
diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts
index 5bc9bb385..4b8aff4c9 100644
--- a/packages/instant/src/util/analytics.ts
+++ b/packages/instant/src/util/analytics.ts
@@ -1,7 +1,16 @@
import { BuyQuote } from '@0x/asset-buyer';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
-import { AffiliateInfo, Asset, Network, OrderSource, ProviderState } from '../types';
+import {
+ AffiliateInfo,
+ Asset,
+ Network,
+ OrderSource,
+ ProviderState,
+ QuoteFetchOrigin,
+ WalletSuggestion,
+} from '../types';
import { EventProperties, heapUtil } from './heap';
@@ -33,10 +42,17 @@ enum EventNames {
BUY_TX_SUBMITTED = 'Buy - Tx Submitted',
BUY_TX_SUCCEEDED = 'Buy - Tx Succeeded',
BUY_TX_FAILED = 'Buy - Tx Failed',
+ INSTALL_WALLET_CLICKED = 'Install Wallet - Clicked',
+ INSTALL_WALLET_MODAL_OPENED = 'Install Wallet - Modal - Opened',
+ INSTALL_WALLET_MODAL_CLICKED_EXPLANATION = 'Install Wallet - Modal - Clicked Explanation',
+ INSTALL_WALLET_MODAL_CLICKED_GET = 'Install Wallet - Modal - Clicked Get',
+ INSTALL_WALLET_MODAL_CLOSED = 'Install Wallet - Modal - Closed',
TOKEN_SELECTOR_OPENED = 'Token Selector - Opened',
TOKEN_SELECTOR_CLOSED = 'Token Selector - Closed',
TOKEN_SELECTOR_CHOSE = 'Token Selector - Chose',
TOKEN_SELECTOR_SEARCHED = 'Token Selector - Searched',
+ QUOTE_FETCHED = 'Quote - Fetched',
+ QUOTE_ERROR = 'Quote - Error',
}
const track = (eventName: EventNames, eventProperties: EventProperties = {}): void => {
@@ -170,6 +186,14 @@ export const analytics = {
expectedTxTimeMs: expectedEndTimeUnix - startTimeUnix,
actualTxTimeMs: new Date().getTime() - startTimeUnix,
}),
+ trackInstallWalletClicked: (walletSuggestion: WalletSuggestion) =>
+ trackingEventFnWithPayload(EventNames.INSTALL_WALLET_CLICKED)({ walletSuggestion }),
+ trackInstallWalletModalClickedExplanation: trackingEventFnWithoutPayload(
+ EventNames.INSTALL_WALLET_MODAL_CLICKED_EXPLANATION,
+ ),
+ trackInstallWalletModalClickedGet: trackingEventFnWithoutPayload(EventNames.INSTALL_WALLET_MODAL_CLICKED_GET),
+ trackInstallWalletModalOpened: trackingEventFnWithoutPayload(EventNames.INSTALL_WALLET_MODAL_OPENED),
+ trackInstallWalletModalClosed: trackingEventFnWithoutPayload(EventNames.INSTALL_WALLET_MODAL_CLOSED),
trackTokenSelectorOpened: trackingEventFnWithoutPayload(EventNames.TOKEN_SELECTOR_OPENED),
trackTokenSelectorClosed: (closedVia: TokenSelectorClosedVia) =>
trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_CLOSED)({ closedVia }),
@@ -177,4 +201,16 @@ export const analytics = {
trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_CHOSE)(payload),
trackTokenSelectorSearched: (searchText: string) =>
trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_SEARCHED)({ searchText }),
+ trackQuoteFetched: (buyQuote: BuyQuote, fetchOrigin: QuoteFetchOrigin) =>
+ trackingEventFnWithPayload(EventNames.QUOTE_FETCHED)({
+ ...buyQuoteEventProperties(buyQuote),
+ fetchOrigin,
+ }),
+ trackQuoteError: (errorMessage: string, assetBuyAmount: BigNumber, fetchOrigin: QuoteFetchOrigin) => {
+ trackingEventFnWithPayload(EventNames.QUOTE_ERROR)({
+ errorMessage,
+ assetBuyAmount: assetBuyAmount.toString(),
+ fetchOrigin,
+ });
+ },
};
diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts
index 2fd16d781..c1899f8c1 100644
--- a/packages/instant/src/util/buy_quote_updater.ts
+++ b/packages/instant/src/util/buy_quote_updater.ts
@@ -6,7 +6,8 @@ import { Dispatch } from 'redux';
import { oc } from 'ts-optchain';
import { Action, actions } from '../redux/actions';
-import { AffiliateInfo, ERC20Asset } from '../types';
+import { AffiliateInfo, ERC20Asset, QuoteFetchOrigin } from '../types';
+import { analytics } from '../util/analytics';
import { assetUtils } from '../util/asset';
import { errorFlasher } from '../util/error_flasher';
@@ -16,7 +17,12 @@ export const buyQuoteUpdater = {
dispatch: Dispatch<Action>,
asset: ERC20Asset,
assetUnitAmount: BigNumber,
- options: { setPending: boolean; dispatchErrors: boolean; affiliateInfo?: AffiliateInfo },
+ fetchOrigin: QuoteFetchOrigin,
+ options: {
+ setPending: boolean;
+ dispatchErrors: boolean;
+ affiliateInfo?: AffiliateInfo;
+ },
): Promise<void> => {
// get a new buy quote.
const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetUnitAmount, asset.metaData.decimals);
@@ -31,6 +37,7 @@ export const buyQuoteUpdater = {
} catch (error) {
if (options.dispatchErrors) {
dispatch(actions.setQuoteRequestStateFailure());
+ analytics.trackQuoteError(error.message ? error.message : 'other', baseUnitValue, fetchOrigin);
let errorMessage;
if (error.message === AssetBuyerError.InsufficientAssetLiquidity) {
const assetName = assetUtils.bestNameForAsset(asset, 'of this asset');
@@ -58,5 +65,6 @@ export const buyQuoteUpdater = {
errorFlasher.clearError(dispatch);
// invalidate the last buy quote.
dispatch(actions.updateLatestBuyQuote(newBuyQuote));
+ analytics.trackQuoteFetched(newBuyQuote, fetchOrigin);
},
};
diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts
index 2b852fb0d..cf29bf3ea 100644
--- a/packages/instant/src/util/heartbeater_factory.ts
+++ b/packages/instant/src/util/heartbeater_factory.ts
@@ -1,5 +1,6 @@
import { asyncData } from '../redux/async_data';
import { Store } from '../redux/store';
+import { QuoteFetchOrigin } from '../types';
import { Heartbeater } from './heartbeater';
@@ -17,8 +18,13 @@ export const generateAccountHeartbeater = (options: HeartbeatFactoryOptions): He
export const generateBuyQuoteHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => {
const { store, shouldPerformImmediatelyOnStart } = options;
return new Heartbeater(async () => {
- await asyncData.fetchCurrentBuyQuoteAndDispatchToStore(store.getState(), store.dispatch, {
- updateSilently: true,
- });
+ await asyncData.fetchCurrentBuyQuoteAndDispatchToStore(
+ store.getState(),
+ store.dispatch,
+ QuoteFetchOrigin.Heartbeat,
+ {
+ updateSilently: true,
+ },
+ );
}, shouldPerformImmediatelyOnStart);
};