Merge pull request #1343 from 0xProject/feature/instant/quote-analytics
[instant] Quote analytics
This commit is contained in:
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@@ -21,6 +21,11 @@ export enum OrderProcessState {
|
||||
Failure = 'FAILURE',
|
||||
}
|
||||
|
||||
export enum QuoteFetchOrigin {
|
||||
Manual = 'Manual',
|
||||
Heartbeat = 'Heartbeat',
|
||||
}
|
||||
|
||||
export interface SimulatedProgress {
|
||||
startTimeUnix: number;
|
||||
expectedEndTimeUnix: number;
|
||||
|
@@ -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, WalletSuggestion } from '../types';
|
||||
import {
|
||||
AffiliateInfo,
|
||||
Asset,
|
||||
Network,
|
||||
OrderSource,
|
||||
ProviderState,
|
||||
QuoteFetchOrigin,
|
||||
WalletSuggestion,
|
||||
} from '../types';
|
||||
|
||||
import { EventProperties, heapUtil } from './heap';
|
||||
|
||||
@@ -42,6 +51,8 @@ enum EventNames {
|
||||
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 => {
|
||||
@@ -190,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,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
@@ -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);
|
||||
},
|
||||
};
|
||||
|
@@ -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);
|
||||
};
|
||||
|
Reference in New Issue
Block a user