|
|
@ -19,6 +19,8 @@ import { |
|
|
|
} from '../../../shared/constants/metametrics'; |
|
|
|
} from '../../../shared/constants/metametrics'; |
|
|
|
import { SECOND } from '../../../shared/constants/time'; |
|
|
|
import { SECOND } from '../../../shared/constants/time'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const EXTENSION_UNINSTALL_URL = 'https://metamask.io/uninstalled'; |
|
|
|
|
|
|
|
|
|
|
|
const defaultCaptureException = (err) => { |
|
|
|
const defaultCaptureException = (err) => { |
|
|
|
// throw error on clean stack so its captured by platform integrations (eg sentry)
|
|
|
|
// throw error on clean stack so its captured by platform integrations (eg sentry)
|
|
|
|
// but does not interrupt the call stack
|
|
|
|
// but does not interrupt the call stack
|
|
|
@ -52,6 +54,9 @@ const exceptionsToFilter = { |
|
|
|
* whether or not events are tracked |
|
|
|
* whether or not events are tracked |
|
|
|
* @property {{[string]: MetaMetricsEventFragment}} [fragments] - Object keyed |
|
|
|
* @property {{[string]: MetaMetricsEventFragment}} [fragments] - Object keyed |
|
|
|
* by UUID with stored fragments as values. |
|
|
|
* 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 { |
|
|
|
export default class MetaMetricsController { |
|
|
@ -69,6 +74,7 @@ export default class MetaMetricsController { |
|
|
|
* identifier from the network controller |
|
|
|
* identifier from the network controller |
|
|
|
* @param {string} options.version - The version of the extension |
|
|
|
* @param {string} options.version - The version of the extension |
|
|
|
* @param {string} options.environment - The environment the extension is running in |
|
|
|
* @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 {MetaMetricsControllerState} options.initState - State to initialized with |
|
|
|
* @param options.captureException |
|
|
|
* @param options.captureException |
|
|
|
*/ |
|
|
|
*/ |
|
|
@ -81,6 +87,7 @@ export default class MetaMetricsController { |
|
|
|
version, |
|
|
|
version, |
|
|
|
environment, |
|
|
|
environment, |
|
|
|
initState, |
|
|
|
initState, |
|
|
|
|
|
|
|
extension, |
|
|
|
captureException = defaultCaptureException, |
|
|
|
captureException = defaultCaptureException, |
|
|
|
}) { |
|
|
|
}) { |
|
|
|
this._captureException = (err) => { |
|
|
|
this._captureException = (err) => { |
|
|
@ -96,12 +103,16 @@ export default class MetaMetricsController { |
|
|
|
this.locale = prefState.currentLocale.replace('_', '-'); |
|
|
|
this.locale = prefState.currentLocale.replace('_', '-'); |
|
|
|
this.version = |
|
|
|
this.version = |
|
|
|
environment === 'production' ? version : `${version}-${environment}`; |
|
|
|
environment === 'production' ? version : `${version}-${environment}`; |
|
|
|
|
|
|
|
this.extension = extension; |
|
|
|
|
|
|
|
this.environment = environment; |
|
|
|
|
|
|
|
|
|
|
|
const abandonedFragments = omitBy(initState?.fragments, 'persist'); |
|
|
|
const abandonedFragments = omitBy(initState?.fragments, 'persist'); |
|
|
|
|
|
|
|
|
|
|
|
this.store = new ObservableStore({ |
|
|
|
this.store = new ObservableStore({ |
|
|
|
participateInMetaMetrics: null, |
|
|
|
participateInMetaMetrics: null, |
|
|
|
metaMetricsId: null, |
|
|
|
metaMetricsId: null, |
|
|
|
|
|
|
|
eventsBeforeMetricsOptIn: [], |
|
|
|
|
|
|
|
traits: {}, |
|
|
|
...initState, |
|
|
|
...initState, |
|
|
|
fragments: { |
|
|
|
fragments: { |
|
|
|
...initState?.fragments, |
|
|
|
...initState?.fragments, |
|
|
@ -315,6 +326,24 @@ export default class MetaMetricsController { |
|
|
|
this._identify(allValidTraits); |
|
|
|
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 |
|
|
|
* Setter for the `participateInMetaMetrics` property |
|
|
|
* |
|
|
|
* |
|
|
@ -331,6 +360,12 @@ export default class MetaMetricsController { |
|
|
|
metaMetricsId = null; |
|
|
|
metaMetricsId = null; |
|
|
|
} |
|
|
|
} |
|
|
|
this.store.updateState({ participateInMetaMetrics, metaMetricsId }); |
|
|
|
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; |
|
|
|
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 */ |
|
|
|
/** PRIVATE METHODS */ |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -549,11 +615,13 @@ export default class MetaMetricsController { |
|
|
|
* @returns {MetaMetricsTraits | null} traits that have changed since last update |
|
|
|
* @returns {MetaMetricsTraits | null} traits that have changed since last update |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
_buildUserTraitsObject(metamaskState) { |
|
|
|
_buildUserTraitsObject(metamaskState) { |
|
|
|
|
|
|
|
const { traits } = this.store.getState(); |
|
|
|
/** @type {MetaMetricsTraits} */ |
|
|
|
/** @type {MetaMetricsTraits} */ |
|
|
|
const currentTraits = { |
|
|
|
const currentTraits = { |
|
|
|
[TRAITS.ADDRESS_BOOK_ENTRIES]: sum( |
|
|
|
[TRAITS.ADDRESS_BOOK_ENTRIES]: sum( |
|
|
|
Object.values(metamaskState.addressBook).map(size), |
|
|
|
Object.values(metamaskState.addressBook).map(size), |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
[TRAITS.INSTALL_DATE_EXT]: traits[TRAITS.INSTALL_DATE_EXT] || '', |
|
|
|
[TRAITS.LEDGER_CONNECTION_TYPE]: metamaskState.ledgerTransportType, |
|
|
|
[TRAITS.LEDGER_CONNECTION_TYPE]: metamaskState.ledgerTransportType, |
|
|
|
[TRAITS.NETWORKS_ADDED]: metamaskState.frequentRpcListDetail.map( |
|
|
|
[TRAITS.NETWORKS_ADDED]: metamaskState.frequentRpcListDetail.map( |
|
|
|
(rpc) => rpc.chainId, |
|
|
|
(rpc) => rpc.chainId, |
|
|
|