1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-22 17:33:23 +01:00

Add more tracking for MetaMask (#15462)

This commit is contained in:
Daniel 2022-08-11 19:33:33 +02:00 committed by GitHub
parent 86f02e4fd5
commit 2eb0fe6978
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 377 additions and 32 deletions

View File

@ -22,6 +22,9 @@ import { SECOND } from '../../shared/constants/time';
import {
REJECT_NOTFICIATION_CLOSE,
REJECT_NOTFICIATION_CLOSE_SIG,
EVENT,
EVENT_NAMES,
TRAITS,
} from '../../shared/constants/metametrics';
import { isManifestV3 } from '../../shared/modules/mv3.utils';
import { maskObject } from '../../shared/modules/object.utils';
@ -69,6 +72,7 @@ let notificationIsOpen = false;
let uiIsTriggering = false;
const openMetamaskTabsIDs = {};
const requestAccountTabIds = {};
let controller;
// state persistence
const inTest = process.env.IN_TEST;
@ -314,7 +318,7 @@ function setupController(initState, initLangCode, remoteSourcePort) {
// MetaMask Controller
//
const controller = new MetamaskController({
controller = new MetamaskController({
infuraProjectId: process.env.INFURA_PROJECT_ID,
// User confirmation callbacks:
showUserConfirmation: triggerUi,
@ -751,12 +755,32 @@ async function openPopup() {
});
}
// It adds the "App Installed" event into a queue of events, which will be tracked only after a user opts into metrics.
const addAppInstalledEvent = () => {
if (controller) {
controller.metaMetricsController.updateTraits({
[TRAITS.INSTALL_DATE_EXT]: new Date().toISOString().split('T')[0], // yyyy-mm-dd
});
controller.metaMetricsController.addEventBeforeMetricsOptIn({
category: EVENT.CATEGORIES.APP,
event: EVENT_NAMES.APP_INSTALLED,
properties: {},
});
return;
}
setTimeout(() => {
// If the controller is not set yet, we wait and try to add the "App Installed" event again.
addAppInstalledEvent();
}, 1000);
};
// On first install, open a new tab with MetaMask
browser.runtime.onInstalled.addListener(({ reason }) => {
if (
reason === 'install' &&
!(process.env.METAMASK_DEBUG || process.env.IN_TEST)
) {
addAppInstalledEvent();
platform.openExtensionInBrowser();
}
});

View File

@ -19,6 +19,8 @@ import {
} from '../../../shared/constants/metametrics';
import { SECOND } from '../../../shared/constants/time';
const EXTENSION_UNINSTALL_URL = 'https://metamask.io/uninstalled';
const defaultCaptureException = (err) => {
// throw error on clean stack so its captured by platform integrations (eg sentry)
// but does not interrupt the call stack
@ -52,6 +54,9 @@ const exceptionsToFilter = {
* whether or not events are tracked
* @property {{[string]: MetaMetricsEventFragment}} [fragments] - Object keyed
* by UUID with stored fragments as values.
* @property {Array} [eventsBeforeMetricsOptIn] - Array of queued events added before
* a user opts into metrics.
* @property {object} [traits] - Traits that are not derived from other state keys.
*/
export default class MetaMetricsController {
@ -69,6 +74,7 @@ export default class MetaMetricsController {
* identifier from the network controller
* @param {string} options.version - The version of the extension
* @param {string} options.environment - The environment the extension is running in
* @param {string} options.extension - webextension-polyfill
* @param {MetaMetricsControllerState} options.initState - State to initialized with
* @param options.captureException
*/
@ -81,6 +87,7 @@ export default class MetaMetricsController {
version,
environment,
initState,
extension,
captureException = defaultCaptureException,
}) {
this._captureException = (err) => {
@ -96,12 +103,16 @@ export default class MetaMetricsController {
this.locale = prefState.currentLocale.replace('_', '-');
this.version =
environment === 'production' ? version : `${version}-${environment}`;
this.extension = extension;
this.environment = environment;
const abandonedFragments = omitBy(initState?.fragments, 'persist');
this.store = new ObservableStore({
participateInMetaMetrics: null,
metaMetricsId: null,
eventsBeforeMetricsOptIn: [],
traits: {},
...initState,
fragments: {
...initState?.fragments,
@ -315,6 +326,24 @@ export default class MetaMetricsController {
this._identify(allValidTraits);
}
// It sets an uninstall URL ("Sorry to see you go!" page),
// which is opened if a user uninstalls the extension.
updateExtensionUninstallUrl(participateInMetaMetrics, metaMetricsId) {
// TODO: Change it to the right URL once it's available.
const query = {};
if (participateInMetaMetrics) {
// We only want to track these things if a user opted into metrics.
query.id = metaMetricsId;
query.env = this.environment;
query.av = this.version;
}
const queryString = new URLSearchParams(query);
this.extension.runtime.setUninstallURL(
`${EXTENSION_UNINSTALL_URL}?${queryString}`,
);
}
/**
* Setter for the `participateInMetaMetrics` property
*
@ -331,6 +360,12 @@ export default class MetaMetricsController {
metaMetricsId = null;
}
this.store.updateState({ participateInMetaMetrics, metaMetricsId });
if (participateInMetaMetrics) {
this.trackEventsAfterMetricsOptIn();
this.clearEventsAfterMetricsOptIn();
}
// TODO: Uncomment the line below once we have a "Sorry to see you go" page ready.
// this.updateExtensionUninstallUrl(participateInMetaMetrics, metaMetricsId);
return metaMetricsId;
}
@ -472,6 +507,37 @@ export default class MetaMetricsController {
}
}
// Track all queued events after a user opted into metrics.
trackEventsAfterMetricsOptIn() {
const { eventsBeforeMetricsOptIn } = this.store.getState();
eventsBeforeMetricsOptIn.forEach((eventBeforeMetricsOptIn) => {
this.trackEvent(eventBeforeMetricsOptIn);
});
}
// Once we track queued events after a user opts into metrics, we want to clear the event queue.
clearEventsAfterMetricsOptIn() {
this.store.updateState({
eventsBeforeMetricsOptIn: [],
});
}
// It adds an event into a queue, which is only tracked if a user opts into metrics.
addEventBeforeMetricsOptIn(event) {
const prevState = this.store.getState().eventsBeforeMetricsOptIn;
this.store.updateState({
eventsBeforeMetricsOptIn: [...prevState, event],
});
}
// Add or update traits for tracking.
updateTraits(newTraits) {
const { traits } = this.store.getState();
this.store.updateState({
traits: { ...traits, ...newTraits },
});
}
/** PRIVATE METHODS */
/**
@ -549,11 +615,13 @@ export default class MetaMetricsController {
* @returns {MetaMetricsTraits | null} traits that have changed since last update
*/
_buildUserTraitsObject(metamaskState) {
const { traits } = this.store.getState();
/** @type {MetaMetricsTraits} */
const currentTraits = {
[TRAITS.ADDRESS_BOOK_ENTRIES]: sum(
Object.values(metamaskState.addressBook).map(size),
),
[TRAITS.INSTALL_DATE_EXT]: traits[TRAITS.INSTALL_DATE_EXT] || '',
[TRAITS.LEDGER_CONNECTION_TYPE]: metamaskState.ledgerTransportType,
[TRAITS.NETWORKS_ADDED]: metamaskState.frequentRpcListDetail.map(
(rpc) => rpc.chainId,

View File

@ -688,6 +688,7 @@ describe('MetaMetricsController', function () {
assert.deepEqual(traits, {
[TRAITS.ADDRESS_BOOK_ENTRIES]: 3,
[TRAITS.INSTALL_DATE_EXT]: '',
[TRAITS.LEDGER_CONNECTION_TYPE]: 'web-hid',
[TRAITS.NETWORKS_ADDED]: [MAINNET_CHAIN_ID, ROPSTEN_CHAIN_ID, '0xaf'],
[TRAITS.NETWORKS_WITHOUT_TICKER]: ['0xaf'],

View File

@ -394,6 +394,7 @@ export default class MetamaskController extends EventEmitter {
),
version: this.platform.getVersion(),
environment: process.env.METAMASK_ENVIRONMENT,
extension: this.extension,
initState: initState.MetaMetricsController,
captureException,
});

View File

@ -183,6 +183,7 @@
* @property {'theme'} THEME - when the user's theme changes we identify the theme trait
* @property {'token_detection_enabled'} TOKEN_DETECTION_ENABLED - when token detection feature is toggled we
* identify the token_detection_enabled trait
* @property {'install_date_ext'} INSTALL_DATE_EXT - when the user installed the extension
*/
/**
@ -192,6 +193,7 @@
export const TRAITS = {
ADDRESS_BOOK_ENTRIES: 'address_book_entries',
INSTALL_DATE_EXT: 'install_date_ext',
LEDGER_CONNECTION_TYPE: 'ledger_connection_type',
NETWORKS_ADDED: 'networks_added',
NETWORKS_WITHOUT_TICKER: 'networks_without_ticker',
@ -201,8 +203,8 @@ export const TRAITS = {
NUMBER_OF_NFTS: 'number_of_nfts',
NUMBER_OF_TOKENS: 'number_of_tokens',
OPENSEA_API_ENABLED: 'opensea_api_enabled',
THREE_BOX_ENABLED: 'three_box_enabled',
THEME: 'theme',
THREE_BOX_ENABLED: 'three_box_enabled',
TOKEN_DETECTION_ENABLED: 'token_detection_enabled',
};
@ -276,12 +278,16 @@ export const REJECT_NOTFICIATION_CLOSE_SIG =
*/
export const EVENT_NAMES = {
ENCRYPTION_PUBLIC_KEY_APPROVED: 'Encryption Public Key Approved',
ENCRYPTION_PUBLIC_KEY_REJECTED: 'Encryption Public Key Rejected',
ENCRYPTION_PUBLIC_KEY_REQUESTED: 'Encryption Public Key Requested',
APP_INSTALLED: 'App Installed',
DECRYPTION_APPROVED: 'Decryption Approved',
DECRYPTION_REJECTED: 'Decryption Rejected',
DECRYPTION_REQUESTED: 'Decryption Requested',
ENCRYPTION_PUBLIC_KEY_APPROVED: 'Encryption Public Key Approved',
ENCRYPTION_PUBLIC_KEY_REJECTED: 'Encryption Public Key Rejected',
ENCRYPTION_PUBLIC_KEY_REQUESTED: 'Encryption Public Key Requested',
NEW_WALLET_CREATED: 'New Wallet Created',
NEW_WALLET_IMPORTED: 'New Wallet Imported',
NFT_ADDED: 'NFT Added',
PERMISSIONS_APPROVED: 'Permissions Approved',
PERMISSIONS_REJECTED: 'Permissions Rejected',
PERMISSIONS_REQUESTED: 'Permissions Requested',
@ -289,10 +295,10 @@ export const EVENT_NAMES = {
SIGNATURE_APPROVED: 'Signature Approved',
SIGNATURE_REJECTED: 'Signature Rejected',
SIGNATURE_REQUESTED: 'Signature Requested',
SUPPORT_LINK_CLICKED: 'Support Link Clicked',
TOKEN_ADDED: 'Token Added',
TOKEN_DETECTED: 'Token Detected',
TOKEN_HIDDEN: 'Token Hidden',
NFT_ADDED: 'NFT Added',
TOKEN_IMPORT_CANCELED: 'Token Import Canceled',
TOKEN_IMPORT_CLICKED: 'Token Import Clicked',
};
@ -300,8 +306,12 @@ export const EVENT_NAMES = {
export const EVENT = {
CATEGORIES: {
ACCOUNTS: 'Accounts',
APP: 'App',
AUTH: 'Auth',
BACKGROUND: 'Background',
ERROR: 'Error',
FOOTER: 'Footer',
HOME: 'Home',
INPAGE_PROVIDER: 'inpage_provider',
MESSAGES: 'Messages',
NAVIGATION: 'Navigation',
@ -314,29 +324,35 @@ export const EVENT = {
TRANSACTIONS: 'Transactions',
WALLET: 'Wallet',
},
LOCATION: {
TOKEN_DETAILS: 'token_details',
TOKEN_DETECTION: 'token_detection',
TOKEN_MENU: 'token_menu',
},
SOURCE: {
NETWORK: {
POPULAR_NETWORK_LIST: 'popular_network_list',
CUSTOM_NETWORK_FORM: 'custom_network_form',
POPULAR_NETWORK_LIST: 'popular_network_list',
},
SWAPS: {
MAIN_VIEW: 'Main View',
TOKEN_VIEW: 'Token View',
},
TRANSACTION: {
USER: 'user',
DAPP: 'dapp',
},
TOKEN: {
CUSTOM: 'custom',
DETECTED: 'detected',
DAPP: 'dapp',
DETECTED: 'detected',
LIST: 'list',
},
},
LOCATION: {
TOKEN_DETECTION: 'token_detection',
TOKEN_MENU: 'token_menu',
TOKEN_DETAILS: 'token_details',
TRANSACTION: {
DAPP: 'dapp',
USER: 'user',
},
},
};
// Values below (e.g. 'location') can be used in the "properties"
// tracking object as keys, e.g. { location: 'Home' }
export const CONTEXT_PROPS = {
PAGE_TITLE: 'location',
};

View File

@ -5,7 +5,11 @@ import Fuse from 'fuse.js';
import InputAdornment from '@material-ui/core/InputAdornment';
import classnames from 'classnames';
import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
import { EVENT } from '../../../../shared/constants/metametrics';
import {
EVENT,
EVENT_NAMES,
CONTEXT_PROPS,
} from '../../../../shared/constants/metametrics';
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import Identicon from '../../ui/identicon';
import SiteIcon from '../../ui/site-icon';
@ -442,6 +446,18 @@ export default class AccountMenu extends Component {
}
<AccountMenuItem
onClick={() => {
trackEvent(
{
category: EVENT.CATEGORIES.NAVIGATION,
event: EVENT_NAMES.SUPPORT_LINK_CLICKED,
properties: {
url: supportLink,
},
},
{
contextPropsIntoEventProperties: [CONTEXT_PROPS.PAGE_TITLE],
},
);
global.platform.openTab({ url: supportLink });
}}
icon={

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import MetaFoxLogo from '../../../ui/metafox-logo';
import PageContainerFooter from '../../../ui/page-container/page-container-footer';
import { EVENT } from '../../../../../shared/constants/metametrics';

View File

@ -17,6 +17,7 @@ import { captureException, captureMessage } from '@sentry/browser';
import { omit } from 'lodash';
import { getEnvironmentType } from '../../app/scripts/lib/util';
import { PATH_NAME_MAP } from '../helpers/constants/routes';
import { CONTEXT_PROPS } from '../../shared/constants/metametrics';
import { useSegmentContext } from '../hooks/useSegmentContext';
import { trackMetaMetricsEvent, trackMetaMetricsPage } from '../store/actions';
@ -57,11 +58,26 @@ export function MetaMetricsProvider({ children }) {
const location = useLocation();
const context = useSegmentContext();
// Sometimes we want to track context properties inside the event's "properties" object.
const addContextPropsIntoEventProperties = (payload, options) => {
const fields = options?.contextPropsIntoEventProperties;
if (!fields || fields.length === 0) {
return;
}
if (!payload.properties) {
payload.properties = {};
}
if (fields.includes(CONTEXT_PROPS.PAGE_TITLE)) {
payload.properties[CONTEXT_PROPS.PAGE_TITLE] = context.page?.title;
}
};
/**
* @type {UITrackEventMethod}
*/
const trackEvent = useCallback(
(payload, options) => {
addContextPropsIntoEventProperties(payload, options);
trackMetaMetricsEvent(
{
...payload,

View File

@ -30,7 +30,8 @@ export function getRenderableTokenData(
if (isSwapsDefaultTokenSymbol(symbol, chainId)) {
contractExchangeRate = 1;
} else if (string && conversionRate > 0) {
// This condition improves performance significantly.
// This condition improves performance significantly, because it only gets a contract exchange rate
// if a token amount is truthy and conversion rate is higher than 0.
contractExchangeRate = contractExchangeRates[toChecksumHexAddress(address)];
}
const formattedFiat =

View File

@ -3,10 +3,16 @@ import PropTypes from 'prop-types';
import { getEnvironmentType } from '../../../app/scripts/lib/util';
import { ENVIRONMENT_TYPE_POPUP } from '../../../shared/constants/app';
import { SUPPORT_REQUEST_LINK } from '../../helpers/constants/common';
import {
EVENT,
EVENT_NAMES,
CONTEXT_PROPS,
} from '../../../shared/constants/metametrics';
class ErrorPage extends PureComponent {
static contextTypes = {
t: PropTypes.func.isRequired,
trackEvent: PropTypes.func,
};
static propTypes = {
@ -41,6 +47,20 @@ class ErrorPage extends PureComponent {
key="metamaskSupportLink"
rel="noopener noreferrer"
href={SUPPORT_REQUEST_LINK}
onClick={() => {
this.context.trackEvent(
{
category: EVENT.CATEGORIES.ERROR,
event: EVENT_NAMES.SUPPORT_LINK_CLICKED,
properties: {
url: SUPPORT_REQUEST_LINK,
},
},
{
contextPropsIntoEventProperties: [CONTEXT_PROPS.PAGE_TITLE],
},
);
}}
>
<span className="error-page__link-text">{this.context.t('here')}</span>
</a>

View File

@ -6,7 +6,11 @@ import MetaFoxLogo from '../../../components/ui/metafox-logo';
import { SUPPORT_REQUEST_LINK } from '../../../helpers/constants/common';
import { DEFAULT_ROUTE } from '../../../helpers/constants/routes';
import { returnToOnboardingInitiatorTab } from '../onboarding-initiator-util';
import { EVENT } from '../../../../shared/constants/metametrics';
import {
EVENT,
EVENT_NAMES,
CONTEXT_PROPS,
} from '../../../../shared/constants/metametrics';
export default class EndOfFlowScreen extends PureComponent {
static contextTypes = {
@ -99,6 +103,20 @@ export default class EndOfFlowScreen extends PureComponent {
key="metamaskSupportLink"
rel="noopener noreferrer"
href={SUPPORT_REQUEST_LINK}
onClick={() => {
this.context.trackEvent(
{
category: EVENT.CATEGORIES.ONBOARDING,
event: EVENT_NAMES.SUPPORT_LINK_CLICKED,
properties: {
url: SUPPORT_REQUEST_LINK,
},
},
{
contextPropsIntoEventProperties: [CONTEXT_PROPS.PAGE_TITLE],
},
);
}}
>
<span className="first-time-flow__link-text">
{this.context.t('here')}

View File

@ -1,11 +1,13 @@
import { connect } from 'react-redux';
import { getOnboardingInitiator } from '../../../selectors';
import { setCompletedOnboarding } from '../../../store/actions';
import { EVENT_NAMES } from '../../../../shared/constants/metametrics';
import EndOfFlow from './end-of-flow.component';
const firstTimeFlowTypeNameMap = {
create: 'New Wallet Created',
import: 'New Wallet Imported',
create: EVENT_NAMES.NEW_WALLET_CREATED,
import: EVENT_NAMES.NEW_WALLET_IMPORTED,
};
const mapStateToProps = (state) => {

View File

@ -10,7 +10,10 @@ import {
DEFAULT_ROUTE,
INITIALIZE_SEED_PHRASE_INTRO_ROUTE,
} from '../../../../helpers/constants/routes';
import { EVENT } from '../../../../../shared/constants/metametrics';
import {
EVENT,
EVENT_NAMES,
} from '../../../../../shared/constants/metametrics';
import { returnToOnboardingInitiatorTab } from '../../onboarding-initiator-util';
import { exportAsFile } from '../../../../../shared/modules/export-utils';
@ -78,6 +81,15 @@ export default class RevealSeedPhrase extends PureComponent {
await Promise.all([setCompletedOnboarding(), setSeedPhraseBackedUp(false)]);
this.context.trackEvent({
category: EVENT.CATEGORIES.ONBOARDING,
event: EVENT_NAMES.NEW_WALLET_CREATED,
properties: {
action: 'Onboarding Complete',
legacy_event: true,
},
});
if (onboardingInitiator) {
await returnToOnboardingInitiatorTab(onboardingInitiator);
}

View File

@ -1,13 +1,39 @@
import React from 'react';
import React, { useContext } from 'react';
import { SUPPORT_REQUEST_LINK } from '../../../helpers/constants/common';
import { useI18nContext } from '../../../hooks/useI18nContext';
import {
EVENT,
EVENT_NAMES,
CONTEXT_PROPS,
} from '../../../../shared/constants/metametrics';
import { MetaMetricsContext } from '../../../contexts/metametrics';
const BetaHomeFooter = () => {
const t = useI18nContext();
const trackEvent = useContext(MetaMetricsContext);
return (
<>
<a href={SUPPORT_REQUEST_LINK} target="_blank" rel="noopener noreferrer">
<a
target="_blank"
rel="noopener noreferrer"
href={SUPPORT_REQUEST_LINK}
onClick={() => {
trackEvent(
{
category: EVENT.CATEGORIES.FOOTER,
event: EVENT_NAMES.SUPPORT_LINK_CLICKED,
properties: {
url: SUPPORT_REQUEST_LINK,
},
},
{
contextPropsIntoEventProperties: [CONTEXT_PROPS.PAGE_TITLE],
},
);
}}
>
{t('needHelpSubmitTicket')}
</a>{' '}
|{' '}

View File

@ -1,13 +1,39 @@
import React from 'react';
import React, { useContext } from 'react';
import { SUPPORT_REQUEST_LINK } from '../../../helpers/constants/common';
import { useI18nContext } from '../../../hooks/useI18nContext';
import {
EVENT,
EVENT_NAMES,
CONTEXT_PROPS,
} from '../../../../shared/constants/metametrics';
import { MetaMetricsContext } from '../../../contexts/metametrics';
const FlaskHomeFooter = () => {
const t = useI18nContext();
const trackEvent = useContext(MetaMetricsContext);
return (
<>
<a href={SUPPORT_REQUEST_LINK} target="_blank" rel="noopener noreferrer">
<a
target="_blank"
rel="noopener noreferrer"
href={SUPPORT_REQUEST_LINK}
onClick={() => {
trackEvent(
{
category: EVENT.CATEGORIES.FOOTER,
event: EVENT_NAMES.SUPPORT_LINK_CLICKED,
properties: {
url: SUPPORT_REQUEST_LINK,
},
},
{
contextPropsIntoEventProperties: [CONTEXT_PROPS.PAGE_TITLE],
},
);
}}
>
{t('needHelpSubmitTicket')}
</a>{' '}
|{' '}

View File

@ -3,6 +3,11 @@ import PropTypes from 'prop-types';
import { Redirect, Route } from 'react-router-dom';
///: BEGIN:ONLY_INCLUDE_IN(main)
import { SUPPORT_LINK } from '../../helpers/constants/common';
import {
EVENT,
EVENT_NAMES,
CONTEXT_PROPS,
} from '../../../shared/constants/metametrics';
///: END:ONLY_INCLUDE_IN
import { formatDate } from '../../helpers/utils/util';
import AssetList from '../../components/app/asset-list';
@ -73,6 +78,7 @@ function shouldCloseNotificationPopup({
export default class Home extends PureComponent {
static contextTypes = {
t: PropTypes.func,
trackEvent: PropTypes.func,
};
static propTypes = {
@ -608,6 +614,7 @@ export default class Home extends PureComponent {
!completedOnboarding) &&
announcementsToShow &&
showWhatsNewPopup;
return (
<div className="main-container">
<Route path={CONNECTED_ROUTE} component={ConnectedSites} exact />
@ -681,6 +688,22 @@ export default class Home extends PureComponent {
target="_blank"
rel="noopener noreferrer"
key="need-help-link"
onClick={() => {
this.context.trackEvent(
{
category: EVENT.CATEGORIES.HOME,
event: EVENT_NAMES.SUPPORT_LINK_CLICKED,
properties: {
url: SUPPORT_LINK,
},
},
{
contextPropsIntoEventProperties: [
CONTEXT_PROPS.PAGE_TITLE,
],
},
);
}}
>
{t('needHelpLinkText')}
</a>,

View File

@ -1,6 +1,7 @@
import React, { useContext } from 'react';
import { useHistory } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import Box from '../../../components/ui/box';
import Typography from '../../../components/ui/typography';
import Button from '../../../components/ui/button';
@ -18,12 +19,12 @@ import {
import { setCompletedOnboarding } from '../../../store/actions';
import { getFirstTimeFlowType } from '../../../selectors';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import { EVENT } from '../../../../shared/constants/metametrics';
import { EVENT, EVENT_NAMES } from '../../../../shared/constants/metametrics';
export default function CreationSuccessful() {
const firstTimeFlowTypeNameMap = {
create: 'New Wallet Created',
import: 'New Wallet Imported',
create: EVENT_NAMES.NEW_WALLET_CREATED,
import: EVENT_NAMES.NEW_WALLET_IMPORTED,
};
const history = useHistory();
const t = useI18nContext();

View File

@ -1,5 +1,6 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import Button from '../../../components/ui/button';
import {
SUPPORT_LINK,
@ -10,6 +11,11 @@ import {
getNumberOfSettingsInSection,
handleSettingsRefs,
} from '../../../helpers/utils/settings-search';
import {
EVENT,
EVENT_NAMES,
CONTEXT_PROPS,
} from '../../../../shared/constants/metametrics';
export default class InfoTab extends PureComponent {
state = {
@ -18,6 +24,7 @@ export default class InfoTab extends PureComponent {
static contextTypes = {
t: PropTypes.func,
trackEvent: PropTypes.func,
};
settingsRefs = Array(
@ -87,6 +94,20 @@ export default class InfoTab extends PureComponent {
target="_blank"
rel="noopener noreferrer"
className="info-tab__link-text"
onClick={() => {
this.context.trackEvent(
{
category: EVENT.CATEGORIES.SETTINGS,
event: EVENT_NAMES.SUPPORT_LINK_CLICKED,
properties: {
url: SUPPORT_LINK,
},
},
{
contextPropsIntoEventProperties: [CONTEXT_PROPS.PAGE_TITLE],
},
);
}}
>
{t('supportCenter')}
</Button>
@ -109,6 +130,20 @@ export default class InfoTab extends PureComponent {
target="_blank"
rel="noopener noreferrer"
className="info-tab__link-text"
onClick={() => {
this.context.trackEvent(
{
category: EVENT.CATEGORIES.SETTINGS,
event: EVENT_NAMES.SUPPORT_LINK_CLICKED,
properties: {
url: SUPPORT_REQUEST_LINK,
},
},
{
contextPropsIntoEventProperties: [CONTEXT_PROPS.PAGE_TITLE],
},
);
}}
>
{t('contactUs')}
</Button>

View File

@ -8,7 +8,11 @@ import { getBlockExplorerLink } from '@metamask/etherscan-link';
import { I18nContext } from '../../../contexts/i18n';
import { SUPPORT_LINK } from '../../../helpers/constants/common';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import { EVENT } from '../../../../shared/constants/metametrics';
import {
EVENT,
EVENT_NAMES,
CONTEXT_PROPS,
} from '../../../../shared/constants/metametrics';
import {
getCurrentChainId,
@ -156,6 +160,20 @@ export default function AwaitingSwap({
href={SUPPORT_LINK}
target="_blank"
rel="noopener noreferrer"
onClick={() => {
trackEvent(
{
category: EVENT.CATEGORIES.SWAPS,
event: EVENT_NAMES.SUPPORT_LINK_CLICKED,
properties: {
url: SUPPORT_LINK,
},
},
{
contextPropsIntoEventProperties: [CONTEXT_PROPS.PAGE_TITLE],
},
);
}}
>
{new URL(SUPPORT_LINK).hostname}
</a>,

View File

@ -7,7 +7,11 @@ import TextField from '../../components/ui/text-field';
import Mascot from '../../components/ui/mascot';
import { SUPPORT_LINK } from '../../helpers/constants/common';
import { DEFAULT_ROUTE } from '../../helpers/constants/routes';
import { EVENT } from '../../../shared/constants/metametrics';
import {
EVENT,
EVENT_NAMES,
CONTEXT_PROPS,
} from '../../../shared/constants/metametrics';
export default class UnlockPage extends Component {
static contextTypes = {
@ -205,6 +209,22 @@ export default class UnlockPage extends Component {
target="_blank"
rel="noopener noreferrer"
key="need-help-link"
onClick={() => {
this.context.trackEvent(
{
category: EVENT.CATEGORIES.NAVIGATION,
event: EVENT_NAMES.SUPPORT_LINK_CLICKED,
properties: {
url: SUPPORT_LINK,
},
},
{
contextPropsIntoEventProperties: [
CONTEXT_PROPS.PAGE_TITLE,
],
},
);
}}
>
{t('needHelpLinkText')}
</a>,