You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
5.8 KiB
210 lines
5.8 KiB
4 years ago
|
/**
|
||
|
* This file is intended to be renamed to metametrics.js once the conversion is complete.
|
||
|
* MetaMetrics is our own brand, and should remain aptly named regardless of the underlying
|
||
|
* metrics system. This file implements Segment analytics tracking.
|
||
|
*/
|
||
4 years ago
|
import React, {
|
||
|
Component,
|
||
|
createContext,
|
||
|
useEffect,
|
||
4 years ago
|
useRef,
|
||
|
useCallback,
|
||
4 years ago
|
} from 'react';
|
||
|
import { useSelector } from 'react-redux';
|
||
|
import PropTypes from 'prop-types';
|
||
|
import { matchPath, useLocation, useRouteMatch } from 'react-router-dom';
|
||
|
import { captureException, captureMessage } from '@sentry/browser';
|
||
4 years ago
|
|
||
4 years ago
|
import { omit } from 'lodash';
|
||
4 years ago
|
import { getEnvironmentType } from '../../app/scripts/lib/util';
|
||
4 years ago
|
import { PATH_NAME_MAP } from '../helpers/constants/routes';
|
||
|
import { txDataSelector } from '../selectors';
|
||
4 years ago
|
|
||
4 years ago
|
import { trackMetaMetricsEvent, trackMetaMetricsPage } from '../store/actions';
|
||
4 years ago
|
|
||
|
// type imports
|
||
|
/**
|
||
4 years ago
|
* @typedef {import('../../shared/constants/metametrics').MetaMetricsEventPayload} MetaMetricsEventPayload
|
||
|
* @typedef {import('../../shared/constants/metametrics').MetaMetricsEventOptions} MetaMetricsEventOptions
|
||
|
* @typedef {import('../../shared/constants/metametrics').MetaMetricsPageObject} MetaMetricsPageObject
|
||
|
* @typedef {import('../../shared/constants/metametrics').MetaMetricsReferrerObject} MetaMetricsReferrerObject
|
||
4 years ago
|
*/
|
||
|
|
||
|
// types
|
||
|
/**
|
||
|
* @typedef {Omit<MetaMetricsEventPayload, 'environmentType' | 'page' | 'referrer'>} UIMetricsEventPayload
|
||
|
*/
|
||
|
/**
|
||
|
* @typedef {(
|
||
|
* payload: UIMetricsEventPayload,
|
||
|
* options: MetaMetricsEventOptions
|
||
|
* ) => Promise<void>} UITrackEventMethod
|
||
|
*/
|
||
4 years ago
|
|
||
4 years ago
|
/**
|
||
|
* @type {React.Context<UITrackEventMethod>}
|
||
|
*/
|
||
4 years ago
|
export const MetaMetricsContext = createContext(() => {
|
||
|
captureException(
|
||
4 years ago
|
Error(
|
||
|
`MetaMetrics context function was called from a react node that is not a descendant of a MetaMetrics context provider`,
|
||
|
),
|
||
4 years ago
|
);
|
||
|
});
|
||
4 years ago
|
|
||
4 years ago
|
const PATHS_TO_CHECK = Object.keys(PATH_NAME_MAP);
|
||
4 years ago
|
|
||
4 years ago
|
/**
|
||
|
* Returns the current page if it matches out route map, as well as the origin
|
||
|
* if there is a confirmation that was triggered by a dapp
|
||
|
* @returns {{
|
||
|
* page?: MetaMetricsPageObject
|
||
|
* referrer?: MetaMetricsReferrerObject
|
||
|
* }}
|
||
|
*/
|
||
4 years ago
|
function useSegmentContext() {
|
||
|
const match = useRouteMatch({
|
||
|
path: PATHS_TO_CHECK,
|
||
|
exact: true,
|
||
|
strict: true,
|
||
4 years ago
|
});
|
||
|
const txData = useSelector(txDataSelector) || {};
|
||
|
const confirmTransactionOrigin = txData.origin;
|
||
4 years ago
|
|
||
4 years ago
|
const referrer = confirmTransactionOrigin
|
||
|
? {
|
||
|
url: confirmTransactionOrigin,
|
||
|
}
|
||
4 years ago
|
: undefined;
|
||
4 years ago
|
|
||
4 years ago
|
const page = match
|
||
|
? {
|
||
|
path: match.path,
|
||
|
title: PATH_NAME_MAP[match.path],
|
||
|
url: match.path,
|
||
|
}
|
||
4 years ago
|
: undefined;
|
||
4 years ago
|
|
||
|
return {
|
||
|
page,
|
||
|
referrer,
|
||
4 years ago
|
};
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
export function MetaMetricsProvider({ children }) {
|
||
4 years ago
|
const location = useLocation();
|
||
|
const context = useSegmentContext();
|
||
4 years ago
|
|
||
|
/**
|
||
4 years ago
|
* @type {UITrackEventMethod}
|
||
4 years ago
|
*/
|
||
4 years ago
|
const trackEvent = useCallback(
|
||
|
(payload, options) => {
|
||
|
trackMetaMetricsEvent(
|
||
|
{
|
||
|
...payload,
|
||
|
environmentType: getEnvironmentType(),
|
||
|
...context,
|
||
|
},
|
||
|
options,
|
||
4 years ago
|
);
|
||
4 years ago
|
},
|
||
|
[context],
|
||
4 years ago
|
);
|
||
4 years ago
|
|
||
|
// Used to prevent double tracking page calls
|
||
4 years ago
|
const previousMatch = useRef();
|
||
4 years ago
|
|
||
|
/**
|
||
|
* Anytime the location changes, track a page change with segment.
|
||
|
* Previously we would manually track changes to history and keep a
|
||
|
* reference to the previous url, but with page tracking we can see
|
||
|
* which page the user is on and their navigation path.
|
||
|
*/
|
||
|
useEffect(() => {
|
||
4 years ago
|
const environmentType = getEnvironmentType();
|
||
4 years ago
|
const match = matchPath(location.pathname, {
|
||
|
path: PATHS_TO_CHECK,
|
||
|
exact: true,
|
||
|
strict: true,
|
||
4 years ago
|
});
|
||
4 years ago
|
// Start by checking for a missing match route. If this falls through to
|
||
|
// the else if, then we know we have a matched route for tracking.
|
||
|
if (!match) {
|
||
|
captureMessage(`Segment page tracking found unmatched route`, {
|
||
|
extra: {
|
||
|
previousMatch,
|
||
|
currentPath: location.pathname,
|
||
|
},
|
||
4 years ago
|
});
|
||
4 years ago
|
} else if (
|
||
|
previousMatch.current !== match.path &&
|
||
|
!(
|
||
|
environmentType === 'notification' &&
|
||
|
match.path === '/' &&
|
||
|
previousMatch.current === undefined
|
||
|
)
|
||
|
) {
|
||
|
// When a notification window is open by a Dapp we do not want to track
|
||
|
// the initial home route load that can sometimes happen. To handle
|
||
|
// this we keep track of the previousMatch, and we skip the event track
|
||
|
// in the event that we are dealing with the initial load of the
|
||
|
// homepage
|
||
4 years ago
|
const { path, params } = match;
|
||
|
const name = PATH_NAME_MAP[path];
|
||
4 years ago
|
trackMetaMetricsPage(
|
||
|
{
|
||
4 years ago
|
name,
|
||
4 years ago
|
// We do not want to send addresses or accounts in any events
|
||
|
// Some routes include these as params.
|
||
|
params: omit(params, ['account', 'address']),
|
||
|
environmentType,
|
||
|
page: context.page,
|
||
|
referrer: context.referrer,
|
||
|
},
|
||
|
{
|
||
|
isOptInPath: location.pathname.startsWith('/initialize'),
|
||
|
},
|
||
4 years ago
|
);
|
||
4 years ago
|
}
|
||
4 years ago
|
previousMatch.current = match?.path;
|
||
|
}, [location, context]);
|
||
4 years ago
|
|
||
|
return (
|
||
|
<MetaMetricsContext.Provider value={trackEvent}>
|
||
|
{children}
|
||
|
</MetaMetricsContext.Provider>
|
||
4 years ago
|
);
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
MetaMetricsProvider.propTypes = { children: PropTypes.node };
|
||
4 years ago
|
|
||
|
export class LegacyMetaMetricsProvider extends Component {
|
||
|
static propTypes = {
|
||
|
children: PropTypes.node,
|
||
4 years ago
|
};
|
||
4 years ago
|
|
||
|
static defaultProps = {
|
||
|
children: undefined,
|
||
4 years ago
|
};
|
||
4 years ago
|
|
||
4 years ago
|
static contextType = MetaMetricsContext;
|
||
4 years ago
|
|
||
|
static childContextTypes = {
|
||
|
// This has to be different than the type name for the old metametrics file
|
||
|
// using the same name would result in whichever was lower in the tree to be
|
||
|
// used.
|
||
|
trackEvent: PropTypes.func,
|
||
4 years ago
|
};
|
||
4 years ago
|
|
||
4 years ago
|
getChildContext() {
|
||
4 years ago
|
return {
|
||
|
trackEvent: this.context,
|
||
4 years ago
|
};
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
render() {
|
||
4 years ago
|
return this.props.children;
|
||
4 years ago
|
}
|
||
|
}
|