Merge pull request #447 from 0xProject/refactorAnalytics

Refactor Website Analytics
This commit is contained in:
Fabio Berger 2018-03-12 11:28:34 +01:00 committed by GitHub
commit 76a31b6fd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 84 additions and 82 deletions

View File

@ -39,6 +39,7 @@ import {
BlockchainErrs,
ContractInstance,
Order as PortalOrder,
Providers,
ProviderType,
Side,
SideToAssetToken,
@ -58,6 +59,12 @@ import * as MintableArtifacts from '../contracts/Mintable.json';
const BLOCK_NUMBER_BACK_TRACK = 50;
const GWEI_IN_WEI = 1000000000;
const providerToName: { [provider: string]: string } = {
[Providers.Metamask]: constants.PROVIDER_NAME_METAMASK,
[Providers.Parity]: constants.PROVIDER_NAME_PARITY_SIGNER,
[Providers.Mist]: constants.PROVIDER_NAME_MIST,
};
export class Blockchain {
public networkId: number;
public nodeVersion: string;
@ -71,18 +78,12 @@ export class Blockchain {
private _ledgerSubprovider: LedgerWalletSubprovider;
private _defaultGasPrice: BigNumber;
private static _getNameGivenProvider(provider: Web3.Provider): string {
if (!_.isUndefined((provider as any).isMetaMask)) {
return constants.PROVIDER_NAME_METAMASK;
const providerType = utils.getProviderType(provider);
const providerNameIfExists = providerToName[providerType];
if (_.isUndefined(providerNameIfExists)) {
return constants.PROVIDER_NAME_GENERIC;
}
// HACK: We use the fact that Parity Signer's provider is an instance of their
// internal `Web3FrameProvider` class.
const isParitySigner = _.startsWith(provider.constructor.toString(), 'function Web3FrameProvider');
if (isParitySigner) {
return constants.PROVIDER_NAME_PARITY_SIGNER;
}
return constants.PROVIDER_NAME_GENERIC;
return providerNameIfExists;
}
private static async _getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number) {
const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3);

View File

@ -7,7 +7,6 @@ import { Card, CardHeader, CardText } from 'material-ui/Card';
import Divider from 'material-ui/Divider';
import RaisedButton from 'material-ui/RaisedButton';
import * as React from 'react';
import * as ReactGA from 'react-ga';
import { Link } from 'react-router-dom';
import { Blockchain } from 'ts/blockchain';
import { TrackTokenConfirmationDialog } from 'ts/components/dialogs/track_token_confirmation_dialog';
@ -22,6 +21,7 @@ import { Dispatcher } from 'ts/redux/dispatcher';
import { portalOrderSchema } from 'ts/schemas/portal_order_schema';
import { validator } from 'ts/schemas/validator';
import { AlertTypes, BlockchainErrs, Order, Token, TokenByAddress, WebsitePaths } from 'ts/types';
import { analytics } from 'ts/utils/analytics';
import { constants } from 'ts/utils/constants';
import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils';
@ -544,12 +544,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
signedOrder,
this.props.orderFillAmount,
);
ReactGA.event({
category: 'Portal',
action: 'Fill Order Success',
label: eventLabel,
value: parsedOrder.signedOrder.takerTokenAmount,
});
analytics.logEvent('Portal', 'Fill Order Success', eventLabel, parsedOrder.signedOrder.takerTokenAmount);
// After fill completes, let's force fetch the token balances
this.props.dispatcher.forceTokenStateRefetch();
this.setState({
@ -563,12 +558,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
this.setState({
isFilling: false,
});
ReactGA.event({
category: 'Portal',
action: 'Fill Order Failure',
label: eventLabel,
value: parsedOrder.signedOrder.takerTokenAmount,
});
analytics.logEvent('Portal', 'Fill Order Failure', eventLabel, parsedOrder.signedOrder.takerTokenAmount);
const errMsg = `${err}`;
if (utils.didUserDenyWeb3Request(errMsg)) {
return;
@ -633,12 +623,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
globalErrMsg: '',
unavailableTakerAmount: takerTokenAmount,
});
ReactGA.event({
category: 'Portal',
action: 'Cancel Order Success',
label: eventLabel,
value: parsedOrder.signedOrder.makerTokenAmount,
});
analytics.logEvent('Portal', 'Cancel Order Success', eventLabel, parsedOrder.signedOrder.makerTokenAmount);
return;
} catch (err) {
this.setState({
@ -648,12 +633,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
if (utils.didUserDenyWeb3Request(errMsg)) {
return;
}
ReactGA.event({
category: 'Portal',
action: 'Cancel Order Failure',
label: eventLabel,
value: parsedOrder.signedOrder.makerTokenAmount,
});
analytics.logEvent('Portal', 'Cancel Order Failure', eventLabel, parsedOrder.signedOrder.makerTokenAmount);
globalErrMsg = 'Failed to cancel order, please refresh and try again';
utils.consoleLog(`${err}`);
this.setState({

View File

@ -5,7 +5,6 @@ import * as _ from 'lodash';
import Dialog from 'material-ui/Dialog';
import Divider from 'material-ui/Divider';
import * as React from 'react';
import * as ReactGA from 'react-ga';
import { Blockchain } from 'ts/blockchain';
import { ExpirationInput } from 'ts/components/inputs/expiration_input';
import { HashInput } from 'ts/components/inputs/hash_input';
@ -21,6 +20,7 @@ import { Dispatcher } from 'ts/redux/dispatcher';
import { portalOrderSchema } from 'ts/schemas/portal_order_schema';
import { validator } from 'ts/schemas/validator';
import { AlertTypes, BlockchainErrs, HashData, Side, SideToAssetToken, Token, TokenByAddress } from 'ts/types';
import { analytics } from 'ts/utils/analytics';
import { constants } from 'ts/utils/constants';
import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils';
@ -256,12 +256,7 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G
if (didSignSuccessfully) {
const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
const eventLabel = `${this.props.tokenByAddress[debitToken.address].symbol}-${networkName}`;
ReactGA.event({
category: 'Portal',
action: 'Sign Order Success',
label: eventLabel,
value: debitToken.amount.toNumber(),
});
analytics.logEvent('Portal', 'Sign Order Success', eventLabel, debitToken.amount.toNumber());
this.setState({
globalErrMsg: '',
shouldShowIncompleteErrs: false,
@ -274,11 +269,7 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G
globalErrMsg = 'You must enable wallet communication';
this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
}
ReactGA.event({
category: 'Portal',
action: 'Sign Order Failure',
label: globalErrMsg,
});
analytics.logEvent('Portal', 'Sign Order Failure', globalErrMsg);
this.setState({
globalErrMsg,
shouldShowIncompleteErrs: true,

View File

@ -3,10 +3,10 @@ import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
import Toggle from 'material-ui/Toggle';
import * as React from 'react';
import * as ReactGA from 'react-ga';
import { Blockchain } from 'ts/blockchain';
import { Dispatcher } from 'ts/redux/dispatcher';
import { BalanceErrs, Token, TokenState } from 'ts/types';
import { analytics } from 'ts/utils/analytics';
import { constants } from 'ts/utils/constants';
import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils';
@ -82,20 +82,10 @@ export class AllowanceToggle extends React.Component<AllowanceToggleProps, Allow
const eventLabel = `${this.props.token.symbol}-${networkName}`;
try {
await this.props.blockchain.setProxyAllowanceAsync(this.props.token, newAllowanceAmountInBaseUnits);
ReactGA.event({
category: 'Portal',
action: 'Set Allowance Success',
label: eventLabel,
value: newAllowanceAmountInBaseUnits.toNumber(),
});
analytics.logEvent('Portal', 'Set Allowance Success', eventLabel, newAllowanceAmountInBaseUnits.toNumber());
await this.props.refetchTokenStateAsync();
} catch (err) {
ReactGA.event({
category: 'Portal',
action: 'Set Allowance Failure',
label: eventLabel,
value: newAllowanceAmountInBaseUnits.toNumber(),
});
analytics.logEvent('Portal', 'Set Allowance Failure', eventLabel, newAllowanceAmountInBaseUnits.toNumber());
this.setState({
isSpinnerVisible: false,
});

View File

@ -2,7 +2,6 @@
import { MuiThemeProvider } from 'material-ui/styles';
import * as React from 'react';
import { render } from 'react-dom';
import * as ReactGA from 'react-ga';
import { Provider } from 'react-redux';
import { BrowserRouter as Router, Redirect, Route, Switch } from 'react-router-dom';
import * as injectTapEventPlugin from 'react-tap-event-plugin';
@ -17,9 +16,9 @@ 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 { WebsitePaths } from 'ts/types';
import { analytics } from 'ts/utils/analytics';
import { muiTheme } from 'ts/utils/mui_theme';
import { utils } from 'ts/utils/utils';
import * as Web3 from 'web3';
import 'whatwg-fetch';
injectTapEventPlugin();
@ -47,13 +46,9 @@ const LazyConnectDocumentation = createLazyComponent('Documentation', async () =
System.import<any>(/* webpackChunkName: "connectDocs" */ 'ts/containers/connect_documentation'),
);
ReactGA.initialize('UA-98720122-1');
window.onload = () => {
const providerName = (window as any).web3
? utils.web3ProviderToString((window as any).web3.currentProvider)
: 'NONE';
ReactGA.ga('set', 'dimension1', providerName);
};
analytics.init();
// tslint:disable-next-line:no-floating-promises
analytics.logProviderAsync((window as any).web3);
const store: ReduxStore<State> = createStore(reducer);
render(
<Router>

View File

@ -463,4 +463,10 @@ export enum Deco {
CapWords,
Upper,
}
export enum Providers {
Parity = 'PARITY',
Metamask = 'METAMASK',
Mist = 'MIST',
}
// tslint:disable:max-file-line-count

View File

@ -0,0 +1,26 @@
import * as _ from 'lodash';
import * as ReactGA from 'react-ga';
import { configs } from 'ts/utils/configs';
import { utils } from 'ts/utils/utils';
import * as Web3 from 'web3';
export const analytics = {
init() {
ReactGA.initialize(configs.GOOGLE_ANALYTICS_ID);
},
logEvent(category: string, action: string, label: string, value?: any) {
ReactGA.event({
category,
action,
label,
value,
});
},
async logProviderAsync(web3IfExists: Web3) {
await utils.onPageLoadAsync();
const providerType = !_.isUndefined(web3IfExists)
? utils.getProviderType(web3IfExists.currentProvider)
: 'NONE';
ReactGA.ga('set', 'dimension1', providerType);
},
};

View File

@ -62,6 +62,7 @@ export const configs = {
TRST: '/images/token_icons/trust.png',
} as { [symbol: string]: string },
IS_MAINNET_ENABLED: true,
GOOGLE_ANALYTICS_ID: 'UA-98720122-1',
LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE: '2017-11-22',
LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE: '2017-12-19',
// NEW_WRAPPED_ETHERS is temporary until we remove the SHOULD_DEPRECATE_OLD_WETH_TOKEN flag

View File

@ -25,6 +25,7 @@ export const constants = {
PROVIDER_NAME_LEDGER: 'Ledger',
PROVIDER_NAME_METAMASK: 'Metamask',
PROVIDER_NAME_PARITY_SIGNER: 'Parity Signer',
PROVIDER_NAME_MIST: 'Mist',
PROVIDER_NAME_GENERIC: 'Injected Web3',
PROVIDER_NAME_PUBLIC: '0x Public',
ROLLBAR_ACCESS_TOKEN: 'a6619002b51c4464928201e6ea94de65',

View File

@ -4,10 +4,11 @@ import { BigNumber } from '@0xproject/utils';
import deepEqual = require('deep-equal');
import * as _ from 'lodash';
import * as moment from 'moment';
import { Order, ScreenWidths, Side, SideToAssetToken, Token, TokenByAddress } from 'ts/types';
import { Order, Providers, ScreenWidths, Side, SideToAssetToken, Token, TokenByAddress } from 'ts/types';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
import * as u2f from 'ts/vendor/u2f_api';
import Web3 = require('web3');
const LG_MIN_EM = 64;
const MD_MIN_EM = 52;
@ -268,17 +269,6 @@ export const utils = {
const baseUrl = `https://${window.location.hostname}${hasPort ? `:${port}` : ''}`;
return baseUrl;
},
web3ProviderToString(provider: Web3Provider): string {
let parsedProviderName = provider.constructor.name;
if (provider.constructor.name === 'MetamaskInpageProvider') {
parsedProviderName = 'METAMASK';
} else if (provider.constructor.name === 'EthereumProvider') {
parsedProviderName = 'MIST';
} else if ((provider as any).isParity) {
parsedProviderName = 'PARITY';
}
return parsedProviderName;
},
async onPageLoadAsync(): Promise<void> {
if (document.readyState === 'complete') {
return; // Already loaded
@ -287,4 +277,25 @@ export const utils = {
window.onload = () => resolve();
});
},
getProviderType(provider: Web3.Provider): Providers | string {
const constructorName = provider.constructor.name;
let parsedProviderName = constructorName;
switch (constructorName) {
case 'MetamaskInpageProvider':
parsedProviderName = Providers.Metamask;
break;
case 'EthereumProvider':
parsedProviderName = Providers.Mist;
break;
default:
parsedProviderName = constructorName;
break;
}
if ((provider as any).isParity) {
parsedProviderName = Providers.Parity;
}
return parsedProviderName;
},
};