diff options
8 files changed, 75 insertions, 15 deletions
diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index efb844cb5..ec2365c11 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -1,3 +1,4 @@ +import { constants as sharedConstants } from '@0xproject/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; @@ -6,9 +7,11 @@ import { Blockchain } from 'ts/blockchain'; import { OnboardingFlow, Step } from 'ts/components/onboarding/onboarding_flow'; import { AllowanceToggle } from 'ts/containers/inputs/allowance_toggle'; import { ProviderType, Token, TokenByAddress, TokenStateByAddress } from 'ts/types'; +import { analytics } from 'ts/utils/analytics'; import { utils } from 'ts/utils/utils'; export interface PortalOnboardingFlowProps { + networkId: number; blockchain: Blockchain; stepIndex: number; isRunning: boolean; @@ -38,8 +41,8 @@ export class PortalOnboardingFlow extends React.Component<PortalOnboardingFlowPr steps={this._getSteps()} stepIndex={this.props.stepIndex} isRunning={this.props.isRunning} - onClose={this.props.updateIsRunning.bind(this, false)} - updateOnboardingStep={this.props.updateOnboardingStep} + onClose={this._closeOnboarding.bind(this)} + updateOnboardingStep={this._updateOnboardingStep.bind(this)} /> ); } @@ -154,9 +157,21 @@ export class PortalOnboardingFlow extends React.Component<PortalOnboardingFlowPr } private _autoStartOnboardingIfShould(): void { if (!this.props.isRunning && !this.props.hasBeenSeen && this.props.blockchainIsLoaded) { + const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; + analytics.logEvent('Portal', 'Onboarding Started - Automatic', networkName, this.props.stepIndex); this.props.updateIsRunning(true); } } + private _updateOnboardingStep(stepIndex: number): void { + const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; + this.props.updateOnboardingStep(stepIndex); + analytics.logEvent('Portal', 'Update Onboarding Step', networkName, stepIndex); + } + private _closeOnboarding(): void { + const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; + this.props.updateIsRunning(false); + analytics.logEvent('Portal', 'Onboarding Closed', networkName, this.props.stepIndex); + } private _renderZrxAllowanceToggle(): React.ReactNode { const zrxToken = utils.getZrxToken(this.props.tokenByAddress); return this._renderAllowanceToggle(zrxToken); diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 48486939b..5170bf1e1 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -1,4 +1,4 @@ -import { colors, Styles } from '@0xproject/react-shared'; +import { colors, constants as sharedConstants, Styles } from '@0xproject/react-shared'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import * as React from 'react'; @@ -44,6 +44,7 @@ import { TokenVisibility, WebsitePaths, } from 'ts/types'; +import { analytics } from 'ts/utils/analytics'; import { backendClient } from 'ts/utils/backend_client'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; @@ -71,6 +72,7 @@ export interface PortalProps { flashMessage?: string | React.ReactNode; lastForceTokenStateRefetch: number; translate: Translate; + portalOnboardingStep: number; } interface PortalState { @@ -362,6 +364,8 @@ export class Portal extends React.Component<PortalProps, PortalState> { } private _startOnboarding(): void { + const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; + analytics.logEvent('Portal', 'Onboarding Started - Manual', networkName, this.props.portalOnboardingStep); this.props.dispatcher.updatePortalOnboardingShowing(true); } private _renderWalletSection(): React.ReactNode { diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index fbb634164..7ff8208b0 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -1,7 +1,8 @@ -import { Styles } from '@0xproject/react-shared'; +import { constants as sharedConstants, Styles } from '@0xproject/react-shared'; import * as _ from 'lodash'; import { GridTile } from 'material-ui/GridList'; import * as React from 'react'; +import { analytics } from 'ts/utils/analytics'; import { TopTokens } from 'ts/components/relayer_index/relayer_top_tokens'; import { Container } from 'ts/components/ui/container'; @@ -64,10 +65,14 @@ export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = ( const link = props.relayerInfo.appUrl || props.relayerInfo.url; const topTokens = props.relayerInfo.topTokens; const weeklyTxnVolume = props.relayerInfo.weeklyTxnVolume; + let trackRelayerClick = (): void => undefined; + const networkName = sharedConstants.NETWORK_NAME_BY_ID[props.networkId]; + const eventLabel = `${props.relayerInfo.name}-${networkName}`; + trackRelayerClick = () => analytics.logEvent('Portal', 'Relayer Click', eventLabel); return ( <Island style={styles.root} Component={GridTile}> <div style={styles.innerDiv}> - <a href={link} target="_blank" style={{ textDecoration: 'none' }}> + <a href={link} target="_blank" style={{ textDecoration: 'none' }} onClick={trackRelayerClick}> <ImgWithFallback src={props.relayerInfo.headerImgUrl} fallbackSrc={FALLBACK_IMG_SRC} diff --git a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx index a5754180b..b599e7123 100644 --- a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx +++ b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx @@ -1,6 +1,13 @@ -import { colors, EtherscanLinkSuffixes, Styles, utils as sharedUtils } from '@0xproject/react-shared'; +import { + colors, + constants as sharedConstants, + EtherscanLinkSuffixes, + Styles, + utils as sharedUtils, +} from '@0xproject/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; +import { analytics } from 'ts/utils/analytics'; import { WebsiteBackendTokenInfo } from 'ts/types'; @@ -61,6 +68,9 @@ class TokenLink extends React.Component<TokenLinkProps, TokenLinkState> { cursor: 'pointer', opacity: this.state.isHovering ? 0.5 : 1, }; + const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; + const eventLabel = `${this.props.tokenInfo.symbol}-${networkName}`; + const trackTokenClick = () => analytics.logEvent('Portal', 'Token Click', eventLabel); return ( <a href={tokenLinkFromToken(this.props.tokenInfo, this.props.networkId)} @@ -68,6 +78,7 @@ class TokenLink extends React.Component<TokenLinkProps, TokenLinkState> { style={style} onMouseEnter={this._onToggleHover.bind(this, true)} onMouseLeave={this._onToggleHover.bind(this, false)} + onClick={trackTokenClick} > {this.props.tokenInfo.symbol} </a> diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 4523b0ac9..fc5e45248 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -1,9 +1,15 @@ -import { EtherscanLinkSuffixes, Styles, utils as sharedUtils } from '@0xproject/react-shared'; +import { + constants as sharedConstants, + EtherscanLinkSuffixes, + Styles, + utils as sharedUtils, +} from '@0xproject/react-shared'; import { BigNumber, errorUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import CircularProgress from 'material-ui/CircularProgress'; import FloatingActionButton from 'material-ui/FloatingActionButton'; + import { ListItem } from 'material-ui/List'; import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet'; import ContentAdd from 'material-ui/svg-icons/content/add'; @@ -35,6 +41,7 @@ import { TokenStateByAddress, WebsitePaths, } from 'ts/types'; +import { analytics } from 'ts/utils/analytics'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles'; @@ -492,18 +499,26 @@ export class Wallet extends React.Component<WalletProps, WalletState> { } } const onClick = isWrappedEtherDirectionOpen - ? this._closeWrappedEtherActionRow.bind(this) + ? this._closeWrappedEtherActionRow.bind(this, wrappedEtherDirection) : this._openWrappedEtherActionRow.bind(this, wrappedEtherDirection); return ( <IconButton iconName={buttonIconName} labelText={buttonLabel} onClick={onClick} color={colors.mediumBlue} /> ); } private _openWrappedEtherActionRow(wrappedEtherDirection: Side): void { + const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; + const action = + wrappedEtherDirection === Side.Deposit ? 'Wallet - Wrap ETH Opened' : 'Wallet - Unwrap WETH Opened'; + analytics.logEvent('Portal', action, networkName); this.setState({ wrappedEtherDirection, }); } - private _closeWrappedEtherActionRow(): void { + private _closeWrappedEtherActionRow(wrappedEtherDirection: Side): void { + const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; + const action = + wrappedEtherDirection === Side.Deposit ? 'Wallet - Wrap ETH Closed' : 'Wallet - Unwrap WETH Closed'; + analytics.logEvent('Portal', action, networkName); this.setState({ wrappedEtherDirection: undefined, }); diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx index f65257142..d6135ce4d 100644 --- a/packages/website/ts/components/wallet/wrap_ether_item.tsx +++ b/packages/website/ts/components/wallet/wrap_ether_item.tsx @@ -1,4 +1,4 @@ -import { Styles } from '@0xproject/react-shared'; +import { constants as sharedConstants, Styles } from '@0xproject/react-shared'; import { BigNumber, logUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; @@ -11,6 +11,7 @@ import { TokenAmountInput } from 'ts/components/inputs/token_amount_input'; import { Dispatcher } from 'ts/redux/dispatcher'; import { colors } from 'ts/style/colors'; import { BlockchainCallErrs, Side, Token } 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'; @@ -186,6 +187,7 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther this.setState({ isEthConversionHappening: true, }); + const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; try { const etherToken = this.props.etherToken; const amountToConvert = this.state.currentInputAmount; @@ -193,10 +195,12 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther await this.props.blockchain.convertEthToWrappedEthTokensAsync(etherToken.address, amountToConvert); const ethAmount = Web3Wrapper.toUnitAmount(amountToConvert, constants.DECIMAL_PLACES_ETH); this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount.toString()} ETH to WETH`); + analytics.logEvent('Portal', 'Wrap ETH Successfully', networkName); } else { await this.props.blockchain.convertWrappedEthTokensToEthAsync(etherToken.address, amountToConvert); const tokenAmount = Web3Wrapper.toUnitAmount(amountToConvert, etherToken.decimals); this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount.toString()} WETH to ETH`); + analytics.logEvent('Portal', 'Unwrap WETH Successfully', networkName); } await this.props.refetchEthTokenStateAsync(); this.props.onConversionSuccessful(); @@ -207,11 +211,13 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther } else if (!utils.didUserDenyWeb3Request(errMsg)) { logUtils.log(`Unexpected error encountered: ${err}`); logUtils.log(err.stack); - const errorMsg = - this.props.direction === Side.Deposit - ? 'Failed to wrap your ETH. Please try again.' - : 'Failed to unwrap your WETH. Please try again.'; - this.props.dispatcher.showFlashMessage(errorMsg); + if (this.props.direction === Side.Deposit) { + this.props.dispatcher.showFlashMessage('Failed to wrap your ETH. Please try again.'); + analytics.logEvent('Portal', 'Wrap ETH Failed', networkName); + } else { + this.props.dispatcher.showFlashMessage('Failed to unwrap your WETH. Please try again.'); + analytics.logEvent('Portal', 'Unwrap WETH Failed', networkName); + } await errorReporter.reportAsync(err); } } diff --git a/packages/website/ts/containers/portal.ts b/packages/website/ts/containers/portal.ts index 5876e65f5..4f33ca1b1 100644 --- a/packages/website/ts/containers/portal.ts +++ b/packages/website/ts/containers/portal.ts @@ -28,6 +28,7 @@ interface ConnectedState { userSuppliedOrderCache: Order; flashMessage?: string | React.ReactNode; translate: Translate; + portalOnboardingStep: number; } interface ConnectedDispatch { @@ -76,6 +77,7 @@ const mapStateToProps = (state: State, _ownProps: PortalComponentProps): Connect userSuppliedOrderCache: state.userSuppliedOrderCache, flashMessage: state.flashMessage, translate: state.translate, + portalOnboardingStep: state.portalOnboardingStep, }; }; diff --git a/packages/website/ts/containers/portal_onboarding_flow.ts b/packages/website/ts/containers/portal_onboarding_flow.ts index 746adf0ba..ba2b8f512 100644 --- a/packages/website/ts/containers/portal_onboarding_flow.ts +++ b/packages/website/ts/containers/portal_onboarding_flow.ts @@ -15,6 +15,7 @@ interface PortalOnboardingFlowProps { } interface ConnectedState { + networkId: number; stepIndex: number; isRunning: boolean; userAddress: string; @@ -32,6 +33,7 @@ interface ConnectedDispatch { } const mapStateToProps = (state: State, _ownProps: PortalOnboardingFlowProps): ConnectedState => ({ + networkId: state.networkId, stepIndex: state.portalOnboardingStep, isRunning: state.isPortalOnboardingShowing, userAddress: state.userAddress, |