diff --git a/app/components/Views/ConnectHardware/SelectHardware/index.tsx b/app/components/Views/ConnectHardware/SelectHardware/index.tsx index 4ce99c7e148..86152efcb32 100644 --- a/app/components/Views/ConnectHardware/SelectHardware/index.tsx +++ b/app/components/Views/ConnectHardware/SelectHardware/index.tsx @@ -18,13 +18,13 @@ import Routes from '../../../../constants/navigation/Routes'; import { MetaMetricsEvents } from '../../../../core/Analytics'; import { getLedgerKeyring } from '../../../../core/Ledger/Ledger'; import { fontStyles } from '../../../../styles/common'; -import AnalyticsV2 from '../../../../util/analyticsV2'; import { mockTheme, useAppThemeFromContext, useAssetFromTheme, } from '../../../../util/theme'; import { getNavigationOptionsTitle } from '../../../UI/Navbar'; +import { useMetrics } from '../../../../components/hooks/useMetrics'; const createStyle = (colors: any) => StyleSheet.create({ @@ -85,6 +85,7 @@ const qrHardwareLogoDark = require(qrHardwareLogoDarkImgPath); const SelectHardwareWallet = () => { const navigation = useNavigation(); + const { trackEvent } = useMetrics(); const { colors } = useAppThemeFromContext() || mockTheme; const styles = createStyle(colors); @@ -107,7 +108,7 @@ const SelectHardwareWallet = () => { const ledgerKeyring = await getLedgerKeyring(); const accounts = await ledgerKeyring.getAccounts(); - AnalyticsV2.trackEvent(MetaMetricsEvents.CONNECT_LEDGER, { + trackEvent(MetaMetricsEvents.CONNECT_LEDGER, { device_type: 'Ledger', }); diff --git a/app/components/Views/ConnectQRHardware/index.tsx b/app/components/Views/ConnectQRHardware/index.tsx index 10aa4b39ff8..565022faa84 100644 --- a/app/components/Views/ConnectQRHardware/index.tsx +++ b/app/components/Views/ConnectQRHardware/index.tsx @@ -16,7 +16,6 @@ import { strings } from '../../../../locales/i18n'; import { UR } from '@ngraveio/bc-ur'; import Alert, { AlertType } from '../../Base/Alert'; import { MetaMetricsEvents } from '../../../core/Analytics'; -import AnalyticsV2 from '../../../util/analyticsV2'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import Device from '../../../util/device'; @@ -26,6 +25,7 @@ import { fontStyles } from '../../../styles/common'; import Logger from '../../../util/Logger'; import { removeAccountsFromPermissions } from '../../../core/Permissions'; import { safeToChecksumAddress } from '../../../util/address'; +import { useMetrics } from '../../../components/hooks/useMetrics'; interface IConnectQRHardwareProps { navigation: any; @@ -74,6 +74,7 @@ const createStyles = (colors: any) => const ConnectQRHardware = ({ navigation }: IConnectQRHardwareProps) => { const { colors } = useTheme(); + const { trackEvent } = useMetrics(); const styles = createStyles(colors); const KeyringController = useMemo(() => { @@ -138,23 +139,20 @@ const ConnectQRHardware = ({ navigation }: IConnectQRHardwareProps) => { }, [QRState.sync, hideScanner, showScanner]); const onConnectHardware = useCallback(async () => { - AnalyticsV2.trackEvent(MetaMetricsEvents.CONTINUE_QR_HARDWARE_WALLET, { + trackEvent(MetaMetricsEvents.CONTINUE_QR_HARDWARE_WALLET, { device_type: 'QR Hardware', }); resetError(); const _accounts = await KeyringController.connectQRHardware(0); setAccounts(_accounts); - }, [KeyringController, resetError]); + }, [KeyringController, resetError, trackEvent]); const onScanSuccess = useCallback( (ur: UR) => { hideScanner(); - AnalyticsV2.trackEvent( - MetaMetricsEvents.CONNECT_HARDWARE_WALLET_SUCCESS, - { - device_type: 'QR Hardware', - }, - ); + trackEvent(MetaMetricsEvents.CONNECT_HARDWARE_WALLET_SUCCESS, { + device_type: 'QR Hardware', + }); if (ur.type === SUPPORTED_UR_TYPE.CRYPTO_HDKEY) { KeyringController.submitQRCryptoHDKey(ur.cbor.toString('hex')); } else { @@ -162,7 +160,7 @@ const ConnectQRHardware = ({ navigation }: IConnectQRHardwareProps) => { } resetError(); }, - [KeyringController, hideScanner, resetError], + [KeyringController, hideScanner, resetError, trackEvent], ); const onScanError = useCallback( diff --git a/app/components/Views/DetectedTokens/index.tsx b/app/components/Views/DetectedTokens/index.tsx index dbc794ac460..3cf5c335fb6 100644 --- a/app/components/Views/DetectedTokens/index.tsx +++ b/app/components/Views/DetectedTokens/index.tsx @@ -16,7 +16,6 @@ import NotificationManager from '../../../core/NotificationManager'; import { strings } from '../../../../locales/i18n'; import Logger from '../../../util/Logger'; import { useTheme } from '../../../util/theme'; -import AnalyticsV2 from '../../../util/analyticsV2'; import { getDecimalChainId } from '../../../util/networks'; import { createNavigationDetails } from '../../../util/navigation/navUtils'; import Routes from '../../../constants/navigation/Routes'; @@ -25,6 +24,7 @@ import { selectChainId } from '../../../selectors/networkController'; import BottomSheet, { BottomSheetRef, } from '../../../component-library/components/BottomSheets/BottomSheet'; +import { useMetrics } from '../../../components/hooks/useMetrics'; const createStyles = (colors: any) => StyleSheet.create({ @@ -68,6 +68,7 @@ interface IgnoredTokensByAddress { const DetectedTokens = () => { const navigation = useNavigation(); + const { trackEvent } = useMetrics(); const sheetRef = useRef(null); const detectedTokens = useSelector(selectDetectedTokens); const chainId = useSelector(selectChainId); @@ -125,7 +126,7 @@ const DetectedTokens = () => { await TokensController.addTokens(tokensToImport); InteractionManager.runAfterInteractions(() => tokensToImport.forEach(({ address, symbol }) => - AnalyticsV2.trackEvent(MetaMetricsEvents.TOKEN_ADDED, { + trackEvent(MetaMetricsEvents.TOKEN_ADDED, { token_address: address, token_symbol: symbol, chain_id: getDecimalChainId(chainId), @@ -145,7 +146,7 @@ const DetectedTokens = () => { } }); }, - [chainId, detectedTokens, ignoredTokens], + [chainId, detectedTokens, ignoredTokens, trackEvent], ); const triggerIgnoreAllTokens = () => { @@ -153,15 +154,14 @@ const DetectedTokens = () => { onConfirm: () => dismissModalAndTriggerAction(true), isHidingAll: true, }); - InteractionManager.runAfterInteractions(() => - AnalyticsV2.trackEvent(MetaMetricsEvents.TOKENS_HIDDEN, { - location: 'token_detection', - token_standard: 'ERC20', - asset_type: 'token', - tokens: detectedTokensForAnalytics, - chain_id: getDecimalChainId(chainId), - }), - ); + + trackEvent(MetaMetricsEvents.TOKENS_HIDDEN, { + location: 'token_detection', + token_standard: 'ERC20', + asset_type: 'token', + tokens: detectedTokensForAnalytics, + chain_id: getDecimalChainId(chainId), + }); }; const triggerImportTokens = async () => { @@ -251,7 +251,7 @@ const DetectedTokens = () => { if (hasPendingAction) { return; } - AnalyticsV2.trackEvent(MetaMetricsEvents.TOKEN_IMPORT_CANCELED, { + trackEvent(MetaMetricsEvents.TOKEN_IMPORT_CANCELED, { source: 'detected', tokens: detectedTokensForAnalytics, chain_id: getDecimalChainId(chainId), diff --git a/app/components/Views/EditAccountName/EditAccountName.tsx b/app/components/Views/EditAccountName/EditAccountName.tsx index 20b2ddc1e06..a5ad060777c 100644 --- a/app/components/Views/EditAccountName/EditAccountName.tsx +++ b/app/components/Views/EditAccountName/EditAccountName.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useNavigation } from '@react-navigation/native'; import { useSelector } from 'react-redux'; -import { InteractionManager, Platform, SafeAreaView } from 'react-native'; +import { Platform, SafeAreaView } from 'react-native'; // External dependencies import Text from '../../../component-library/components/Texts/Text/Text'; @@ -22,7 +22,6 @@ import { useStyles } from '../../../component-library/hooks'; import { getEditAccountNameNavBarOptions } from '../../../components/UI/Navbar'; import Engine from '../../../core/Engine'; import generateTestId from '../../../../wdio/utils/generateTestId'; -import Analytics from '../../../core/Analytics/Analytics'; import { MetaMetricsEvents } from '../../../core/Analytics'; import { selectChainId } from '../../../selectors/networkController'; import { @@ -38,9 +37,11 @@ import { useTheme } from '../../../util/theme'; // Internal dependencies import styleSheet from './EditAccountName.styles'; import { getDecimalChainId } from '../../../util/networks'; +import { useMetrics } from '../../../components/hooks/useMetrics'; const EditAccountName = () => { const { colors } = useTheme(); + const { trackEvent } = useMetrics(); const { styles } = useStyles(styleSheet, {}); const { setOptions, goBack, navigate } = useNavigation(); const [accountName, setAccountName] = useState(); @@ -81,26 +82,22 @@ const EditAccountName = () => { setAccountName(name); }; - const saveAccountName = () => { + const saveAccountName = async () => { const { PreferencesController } = Engine.context; PreferencesController.setAccountLabel(selectedAddress, accountName); navigate('WalletView'); - InteractionManager.runAfterInteractions(() => { - try { - const analyticsProperties = async () => { - const accountType = getAddressAccountType(selectedAddress); - const account_type = accountType === 'QR' ? 'hardware' : accountType; - return { account_type, chain_id: getDecimalChainId(chainId) }; - }; - Analytics.trackEventWithParameters( - MetaMetricsEvents.ACCOUNT_RENAMED, - analyticsProperties(), - ); - } catch { - return {}; - } - }); + try { + const analyticsProperties = async () => { + const accountType = getAddressAccountType(selectedAddress); + const account_type = accountType === 'QR' ? 'hardware' : accountType; + return { account_type, chain_id: getDecimalChainId(chainId) }; + }; + const analyticsProps = await analyticsProperties(); + trackEvent(MetaMetricsEvents.ACCOUNT_RENAMED, analyticsProps); + } catch { + return {}; + } }; return ( diff --git a/app/components/Views/ImportFromSecretRecoveryPhrase/index.js b/app/components/Views/ImportFromSecretRecoveryPhrase/index.js index 381908cd031..fe4a4992d7d 100644 --- a/app/components/Views/ImportFromSecretRecoveryPhrase/index.js +++ b/app/components/Views/ImportFromSecretRecoveryPhrase/index.js @@ -63,7 +63,7 @@ import { import navigateTermsOfUse from '../../../util/termsOfUse/termsOfUse'; import { ImportFromSeedSelectorsIDs } from '../../../../e2e/selectors/Onboarding/ImportFromSeed.selectors'; import { ChoosePasswordSelectorsIDs } from '../../../../e2e/selectors/Onboarding/ChoosePassword.selectors'; -import trackAfterInteractions from '../../../util/metrics/TrackAfterInteraction/trackAfterInteractions'; +import trackOnboarding from '../../../util/metrics/TrackOnboarding/trackOnboarding'; const MINIMUM_SUPPORTED_CLIPBOARD_VERSION = 9; @@ -105,9 +105,7 @@ const ImportFromSecretRecoveryPhrase = ({ const confirmPasswordInput = React.createRef(); const track = (event, properties) => { - trackAfterInteractions(event, properties).catch(() => { - Logger.log('ImportFromSecretRecoveryPhrase', `Failed to track ${event}`); - }); + trackOnboarding(event, properties); }; const updateNavBar = () => { diff --git a/app/components/Views/LedgerAccountInfo/index.tsx b/app/components/Views/LedgerAccountInfo/index.tsx index 6d1ddb28eab..439ba4a2df1 100644 --- a/app/components/Views/LedgerAccountInfo/index.tsx +++ b/app/components/Views/LedgerAccountInfo/index.tsx @@ -30,8 +30,8 @@ import AccountDetails from '../../../components/UI/HardwareWallet/AccountDetails import ledgerDeviceDarkImage from '../../../images/ledger-device-dark.png'; import ledgerDeviceLightImage from '../../../images/ledger-device-light.png'; -import AnalyticsV2 from '../../../util/analyticsV2'; import { MetaMetricsEvents } from '../../../core/Analytics'; +import { useMetrics } from '../../../components/hooks/useMetrics'; const createStyles = (colors: any) => StyleSheet.create({ @@ -79,6 +79,7 @@ const createStyles = (colors: any) => const LedgerAccountInfo = () => { const dispatch = useDispatch(); const navigation = useNavigation(); + const { trackEvent } = useMetrics(); const [account, setAccount] = useState(''); const [accountBalance, setAccountBalance] = useState('0'); const { colors } = useAppThemeFromContext() ?? mockTheme; @@ -118,7 +119,7 @@ const LedgerAccountInfo = () => { const onForgetDevice = async () => { await forgetLedger(); dispatch(setReloadAccounts(true)); - AnalyticsV2.trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { + trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { device_type: 'Ledger', }); navigation.dispatch(StackActions.pop(2)); diff --git a/app/components/Views/LedgerConnect/index.tsx b/app/components/Views/LedgerConnect/index.tsx index 4c2e53fdd01..1db5cc6b9d4 100644 --- a/app/components/Views/LedgerConnect/index.tsx +++ b/app/components/Views/LedgerConnect/index.tsx @@ -32,13 +32,13 @@ import LedgerConnectionError, { import { getNavigationOptionsTitle } from '../../UI/Navbar'; import { unlockLedgerDefaultAccount } from '../../../core/Ledger/Ledger'; import { MetaMetricsEvents } from '../../../core/Analytics'; -import AnalyticsV2 from '../../../util/analyticsV2'; import { LEDGER_SUPPORT_LINK } from '../../../constants/urls'; import ledgerDeviceDarkImage from '../../../images/ledger-device-dark.png'; import ledgerDeviceLightImage from '../../../images/ledger-device-light.png'; import ledgerConnectLightImage from '../../../images/ledger-connect-light.png'; import ledgerConnectDarkImage from '../../../images/ledger-connect-dark.png'; +import { useMetrics } from '../../../components/hooks/useMetrics'; const createStyles = (theme: any) => StyleSheet.create({ @@ -114,6 +114,7 @@ const createStyles = (theme: any) => const LedgerConnect = () => { const { AccountTrackerController } = Engine.context as any; const theme = useAppThemeFromContext() ?? mockTheme; + const { trackEvent } = useMetrics(); const navigation = useNavigation(); const styles = useMemo(() => createStyles(theme), [theme]); const [selectedDevice, setSelectedDevice] = useState(null); @@ -137,13 +138,13 @@ const LedgerConnect = () => { const connectLedger = () => { setLoading(true); - AnalyticsV2.trackEvent(MetaMetricsEvents.CONTINUE_LEDGER_HARDWARE_WALLET, { + trackEvent(MetaMetricsEvents.CONTINUE_LEDGER_HARDWARE_WALLET, { device_type: 'Ledger', }); ledgerLogicToRun(async () => { const account = await unlockLedgerDefaultAccount(true); await AccountTrackerController.syncBalanceWithAddresses([account]); - AnalyticsV2.trackEvent(MetaMetricsEvents.CONNECT_LEDGER_SUCCESS, { + trackEvent(MetaMetricsEvents.CONNECT_LEDGER_SUCCESS, { device_type: 'Ledger', }); navigation.dispatch(StackActions.pop(2)); diff --git a/app/components/Views/LockScreen/index.js b/app/components/Views/LockScreen/index.js index 96a1542ab28..e04ccfdd933 100644 --- a/app/components/Views/LockScreen/index.js +++ b/app/components/Views/LockScreen/index.js @@ -13,7 +13,6 @@ import { connect } from 'react-redux'; import LottieView from 'lottie-react-native'; import { baseStyles } from '../../../styles/common'; import Logger from '../../../util/Logger'; -import { trackErrorAsAnalytics } from '../../../util/analyticsV2'; import { Authentication } from '../../../core'; import { getAssetFromTheme, @@ -23,6 +22,7 @@ import { import Routes from '../../../constants/navigation/Routes'; import { selectSelectedAddress } from '../../../selectors/preferencesController'; import { CommonActions } from '@react-navigation/native'; +import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics'; const LOGO_SIZE = 175; const createStyles = (colors) => diff --git a/app/components/Views/Login/index.js b/app/components/Views/Login/index.js index d7825b3393d..50282f4b630 100644 --- a/app/components/Views/Login/index.js +++ b/app/components/Views/Login/index.js @@ -42,10 +42,6 @@ import { import Routes from '../../../constants/navigation/Routes'; import { passwordRequirementsMet } from '../../../util/password'; import ErrorBoundary from '../ErrorBoundary'; -import { - trackErrorAsAnalytics, - trackEventV2 as trackEvent, -} from '../../../util/analyticsV2'; import { toLowerCaseEquals } from '../../../util/general'; import DefaultPreference from 'react-native-default-preference'; import { Authentication } from '../../../core'; @@ -61,6 +57,8 @@ import { MetaMetricsEvents } from '../../../core/Analytics'; import { selectSelectedAddress } from '../../../selectors/preferencesController'; import { RevealSeedViewSelectorsIDs } from '../../../../e2e/selectors/Settings/SecurityAndPrivacy/RevealSeedView.selectors'; import { LoginViewSelectors } from '../../../../e2e/selectors/LoginView.selectors'; +import { withMetricsAwareness } from '../../../components/hooks/useMetrics'; +import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics'; const deviceHeight = Device.getDeviceHeight(); const breakPoint = deviceHeight < 700; @@ -222,6 +220,10 @@ class Login extends PureComponent { * Action to set if the user is using remember me */ setAllowLoginWithRememberMe: PropTypes.func, + /** + * Metrics injected by withMetricsAwareness HOC + */ + metrics: PropTypes.object, }; state = { @@ -243,7 +245,7 @@ class Login extends PureComponent { fieldRef = React.createRef(); async componentDidMount() { - trackEvent(MetaMetricsEvents.LOGIN_SCREEN_VIEWED); + this.props.metrics.trackEvent(MetaMetricsEvents.LOGIN_SCREEN_VIEWED); BackHandler.addEventListener('hardwareBackPress', this.handleBackPress); const authData = await Authentication.getType(); @@ -640,4 +642,7 @@ const mapDispatchToProps = (dispatch) => ({ dispatch(setAllowLoginWithRememberMe(enabled)), }); -export default connect(mapStateToProps, mapDispatchToProps)(Login); +export default connect( + mapStateToProps, + mapDispatchToProps, +)(withMetricsAwareness(Login)); diff --git a/app/components/Views/ManualBackupStep1/index.js b/app/components/Views/ManualBackupStep1/index.js index c804c305a33..378d08d6f60 100644 --- a/app/components/Views/ManualBackupStep1/index.js +++ b/app/components/Views/ManualBackupStep1/index.js @@ -35,8 +35,8 @@ import { createStyles } from './styles'; import { MetaMetricsEvents } from '../../../core/Analytics'; import { Authentication } from '../../../core'; -import trackAfterInteractions from '../../../util/metrics/TrackAfterInteraction/trackAfterInteractions'; import { ManualBackUpStepsSelectorsIDs } from '../../../../e2e/selectors/Onboarding/ManualBackUpSteps.selectors'; +import trackOnboarding from '../../../util/metrics/TrackOnboarding/trackOnboarding'; /** * View that's shown during the second step of @@ -71,9 +71,7 @@ const ManualBackupStep1 = ({ route, navigation, appTheme }) => { }; const track = (event, properties) => { - trackAfterInteractions(event, properties).catch(() => { - Logger.log('ManualBackupStep1', `Failed to track ${event}`); - }); + trackOnboarding(event, properties); }; useEffect(() => { diff --git a/app/components/Views/ManualBackupStep2/index.js b/app/components/Views/ManualBackupStep2/index.js index 79fc1f626aa..123341804c9 100644 --- a/app/components/Views/ManualBackupStep2/index.js +++ b/app/components/Views/ManualBackupStep2/index.js @@ -20,9 +20,8 @@ import { shuffle, compareMnemonics } from '../../../util/mnemonic'; import { MetaMetricsEvents } from '../../../core/Analytics'; import { useTheme } from '../../../util/theme'; import createStyles from './styles'; -import trackAfterInteractions from '../../../util/metrics/TrackAfterInteraction/trackAfterInteractions'; -import Logger from '../../../util/Logger'; import { ManualBackUpStepsSelectorsIDs } from '../../../../e2e/selectors/Onboarding/ManualBackUpSteps.selectors'; +import trackOnboarding from '../../../util/metrics/TrackOnboarding/trackOnboarding'; const ManualBackupStep2 = ({ navigation, seedphraseBackedUp, route }) => { const { colors } = useTheme(); @@ -48,9 +47,7 @@ const ManualBackupStep2 = ({ navigation, seedphraseBackedUp, route }) => { }; const track = (event, properties) => { - trackAfterInteractions(event, properties).catch(() => { - Logger.log('ManualBackupStep2', `Failed to track ${event}`); - }); + trackOnboarding(event, properties); }; const updateNavBar = useCallback(() => { diff --git a/app/components/Views/ManualBackupStep3/index.js b/app/components/Views/ManualBackupStep3/index.js index e71a2767d17..a5e380f60ae 100644 --- a/app/components/Views/ManualBackupStep3/index.js +++ b/app/components/Views/ManualBackupStep3/index.js @@ -30,9 +30,8 @@ import { import { MetaMetricsEvents } from '../../../core/Analytics'; import DefaultPreference from 'react-native-default-preference'; import { ThemeContext, mockTheme } from '../../../util/theme'; -import trackAfterInteractions from '../../../util/metrics/TrackAfterInteraction/trackAfterInteractions'; -import Logger from '../../../util/Logger'; import { ManualBackUpStepsSelectorsIDs } from '../../../../e2e/selectors/Onboarding/ManualBackUpSteps.selectors'; +import trackOnboarding from '../../../util/metrics/TrackOnboarding/trackOnboarding'; const createStyles = (colors) => StyleSheet.create({ @@ -119,9 +118,7 @@ class ManualBackupStep3 extends PureComponent { }; track = (event, properties) => { - trackAfterInteractions(event, properties).catch(() => { - Logger.log('ManualBackupStep3', `Failed to track ${event}`); - }); + trackOnboarding(event, properties); }; updateNavBar = () => { diff --git a/app/components/Views/NetworkSelector/NetworkSelector.tsx b/app/components/Views/NetworkSelector/NetworkSelector.tsx index 15f70fa600c..500c1488b4e 100644 --- a/app/components/Views/NetworkSelector/NetworkSelector.tsx +++ b/app/components/Views/NetworkSelector/NetworkSelector.tsx @@ -37,7 +37,6 @@ import { ButtonWidthTypes, } from '../../../component-library/components/Buttons/Button'; import Engine from '../../../core/Engine'; -import analyticsV2 from '../../../util/analyticsV2'; import { MetaMetricsEvents } from '../../../core/Analytics'; import Routes from '../../../constants/navigation/Routes'; import generateTestId from '../../../../wdio/utils/generateTestId'; @@ -53,6 +52,7 @@ import { TextVariant, } from '../../../component-library/components/Texts/Text'; import { updateIncomingTransactions } from '../../../util/transaction-controller'; +import { useMetrics } from '../../../components/hooks/useMetrics'; // Internal dependencies import styles from './NetworkSelector.styles'; @@ -60,6 +60,7 @@ import styles from './NetworkSelector.styles'; const NetworkSelector = () => { const { navigate } = useNavigation(); const theme = useTheme(); + const { trackEvent } = useMetrics(); const { colors } = theme; const sheetRef = useRef(null); const showTestNetworks = useSelector(selectShowTestNetworks); @@ -84,7 +85,7 @@ const NetworkSelector = () => { sheetRef.current?.onCloseBottomSheet(); - analyticsV2.trackEvent(MetaMetricsEvents.NETWORK_SWITCHED, { + trackEvent(MetaMetricsEvents.NETWORK_SWITCHED, { chain_id: getDecimalChainId(providerConfig.chainId), from_network: providerConfig.type === 'rpc' @@ -110,7 +111,7 @@ const NetworkSelector = () => { NetworkController.setActiveNetwork(networkConfigurationId); sheetRef.current?.onCloseBottomSheet(); - analyticsV2.trackEvent(MetaMetricsEvents.NETWORK_SWITCHED, { + trackEvent(MetaMetricsEvents.NETWORK_SWITCHED, { chain_id: getDecimalChainId(providerConfig.chainId), from_network: providerConfig.type, to_network: nickname, diff --git a/app/components/Views/Onboarding/index.js b/app/components/Views/Onboarding/index.js index bd95e63f98e..bcbadbc9c3b 100644 --- a/app/components/Views/Onboarding/index.js +++ b/app/components/Views/Onboarding/index.js @@ -26,7 +26,6 @@ import { strings } from '../../../../locales/i18n'; import Button from '@metamask/react-native-button'; import { connect } from 'react-redux'; import FadeOutOverlay from '../../UI/FadeOutOverlay'; -import { saveOnboardingEvent } from '../../../actions/onboarding'; import { getTransparentBackOnboardingNavbarOptions, getTransparentOnboardingNavbarOptions, @@ -48,6 +47,7 @@ import { OnboardingSelectorIDs } from '../../../../e2e/selectors/Onboarding/Onbo import Routes from '../../../constants/navigation/Routes'; import { selectAccounts } from '../../../selectors/accountTrackerController'; +import trackOnboarding from '../../../util/metrics/TrackOnboarding/trackOnboarding'; const createStyles = (colors) => StyleSheet.create({ @@ -137,10 +137,6 @@ class Onboarding extends PureComponent { * redux flag that indicates if the user set a password */ passwordSet: PropTypes.bool, - /** - * Save onboarding event to state - */ - saveOnboardingEvent: PropTypes.func, /** * loading status */ @@ -320,14 +316,7 @@ class Onboarding extends PureComponent { }; track = (event) => { - InteractionManager.runAfterInteractions(async () => { - const { metrics } = this.props; - if (metrics.isEnabled()) { - metrics.trackEvent(event); - } else { - this.props.saveOnboardingEvent(event.category); - } - }); + trackOnboarding(event); }; alertExistingUser = (callback) => { @@ -496,7 +485,6 @@ const mapStateToProps = (state) => ({ const mapDispatchToProps = (dispatch) => ({ setLoading: (msg) => dispatch(loadingSet(msg)), unsetLoading: () => dispatch(loadingUnset()), - saveOnboardingEvent: (event) => dispatch(saveOnboardingEvent(event)), }); export default connect( diff --git a/app/components/Views/OnboardingCarousel/index.js b/app/components/Views/OnboardingCarousel/index.js index 248e1c6ac9b..a2c4b29d44b 100644 --- a/app/components/Views/OnboardingCarousel/index.js +++ b/app/components/Views/OnboardingCarousel/index.js @@ -24,8 +24,8 @@ import { ThemeContext, mockTheme } from '../../../util/theme'; import { WELCOME_SCREEN_CAROUSEL_TITLE_ID } from '../../../../wdio/screen-objects/testIDs/Screens/WelcomeScreen.testIds'; import { OnboardingCarouselSelectorIDs } from '../../../../e2e/selectors/Onboarding/OnboardingCarousel.selectors'; import generateTestId from '../../../../wdio/utils/generateTestId'; -import trackAfterInteractions from '../../../util/metrics/TrackAfterInteraction/trackAfterInteractions'; -import Logger from '../../../util/Logger'; +import trackOnboarding from '../../../util/metrics/TrackOnboarding/trackOnboarding'; + const IMAGE_3_RATIO = 215 / 315; const IMAGE_2_RATIO = 222 / 239; const IMAGE_1_RATIO = 285 / 203; @@ -142,13 +142,7 @@ class OnboardingCarousel extends PureComponent { }; track = (event, properties) => { - trackAfterInteractions( - event, - properties, - this.props.saveOnboardingEvent, - ).catch(() => { - Logger.log('OnboardingCarousel', `Failed to track ${event}`); - }); + trackOnboarding(event, properties, this.props.saveOnboardingEvent); }; onPressGetStarted = () => { diff --git a/app/components/Views/Quiz/SRPQuiz/SRPQuiz.tsx b/app/components/Views/Quiz/SRPQuiz/SRPQuiz.tsx index f57d562cd09..d1a0816abdf 100644 --- a/app/components/Views/Quiz/SRPQuiz/SRPQuiz.tsx +++ b/app/components/Views/Quiz/SRPQuiz/SRPQuiz.tsx @@ -10,7 +10,6 @@ import Icon, { } from '../../../../component-library/components/Icons/Icon'; import { useStyles } from '../../../hooks/useStyles'; import { strings } from '../../../../../locales/i18n'; -import AnalyticsV2 from '../../../../util/analyticsV2'; import { MetaMetricsEvents } from '../../../../core/Analytics'; import Routes from '../../../../constants/navigation/Routes'; import { SRP_GUIDE_URL } from '../../../../constants/urls'; @@ -18,6 +17,7 @@ import { SRP_GUIDE_URL } from '../../../../constants/urls'; import { QuizStage } from '../types'; import { QuizContent } from '../QuizContent'; import stylesheet from './styles'; +import { useMetrics } from '../../../../components/hooks/useMetrics'; const introductionImg = require('../../../../images/reveal-srp.png'); @@ -27,6 +27,7 @@ const SRPQuiz = () => { const { styles, theme } = useStyles(stylesheet, {}); const { colors } = theme; const navigation = useNavigation(); + const { trackEvent } = useMetrics(); const dismissModal = (): void => { modalRef.current?.dismissModal(); @@ -66,16 +67,16 @@ const SRPQuiz = () => { ); const goToRevealPrivateCredential = useCallback((): void => { - AnalyticsV2.trackEvent(MetaMetricsEvents.REVEAL_SRP_INITIATED, {}); - AnalyticsV2.trackEvent(MetaMetricsEvents.REVEAL_SRP_CTA, {}); + trackEvent(MetaMetricsEvents.REVEAL_SRP_INITIATED, {}); + trackEvent(MetaMetricsEvents.REVEAL_SRP_CTA, {}); navigation.navigate(Routes.SETTINGS.REVEAL_PRIVATE_CREDENTIAL, { credentialName: 'seed_phrase', shouldUpdateNav: true, }); - }, [navigation]); + }, [navigation, trackEvent]); const introduction = useCallback(() => { - AnalyticsV2.trackEvent(MetaMetricsEvents.SRP_REVEAL_QUIZ_PROMPT_SEEN, {}); + trackEvent(MetaMetricsEvents.SRP_REVEAL_QUIZ_PROMPT_SEEN, {}); return ( { { label: strings('srp_security_quiz.get_started'), onPress: () => { - AnalyticsV2.trackEvent( - MetaMetricsEvents.SRP_REVEAL_START_CTA_SELECTED, - {}, - ); + trackEvent(MetaMetricsEvents.SRP_REVEAL_START_CTA_SELECTED, {}); setStage(QuizStage.questionOne); }, variant: ButtonVariants.Primary, @@ -104,13 +102,10 @@ const SRPQuiz = () => { dismiss={dismissModal} /> ); - }, []); + }, [trackEvent]); const questionOne = useCallback((): Element => { - AnalyticsV2.trackEvent( - MetaMetricsEvents.SRP_REVEAL_FIRST_QUESTION_SEEN, - {}, - ); + trackEvent(MetaMetricsEvents.SRP_REVEAL_FIRST_QUESTION_SEEN, {}); return ( { dismiss={dismissModal} /> ); - }, []); + }, [trackEvent]); const rightAnswerQuestionOne = useCallback((): Element => { - AnalyticsV2.trackEvent( - MetaMetricsEvents.SRP_REVEAL_FIRST_QUESTION_RIGHT_ASNWER, - {}, - ); + trackEvent(MetaMetricsEvents.SRP_REVEAL_FIRST_QUESTION_RIGHT_ASNWER, {}); return ( { dismiss={dismissModal} /> ); - }, [rightAnswerIcon, styles.rightText]); + }, [rightAnswerIcon, styles.rightText, trackEvent]); const wrongAnswerQuestionOne = useCallback((): Element => { - AnalyticsV2.trackEvent( - MetaMetricsEvents.SRP_REVEAL_FIRST_QUESTION_WRONG_ANSWER, - {}, - ); + trackEvent(MetaMetricsEvents.SRP_REVEAL_FIRST_QUESTION_WRONG_ANSWER, {}); return ( { dismiss={dismissModal} /> ); - }, [styles.wrongText, wrongAnswerIcon]); + }, [styles.wrongText, wrongAnswerIcon, trackEvent]); const questionTwo = useCallback((): Element => { - AnalyticsV2.trackEvent( - MetaMetricsEvents.SRP_REVEAL_SECOND_QUESTION_SEEN, - {}, - ); + trackEvent(MetaMetricsEvents.SRP_REVEAL_SECOND_QUESTION_SEEN, {}); return ( { dismiss={dismissModal} /> ); - }, []); + }, [trackEvent]); const rightAnswerQuestionTwo = useCallback((): Element => { - AnalyticsV2.trackEvent( - MetaMetricsEvents.SRP_REVEAL_SECOND_QUESTION_RIGHT_ASNWER, - {}, - ); + trackEvent(MetaMetricsEvents.SRP_REVEAL_SECOND_QUESTION_RIGHT_ASNWER, {}); return ( { dismiss={dismissModal} /> ); - }, [goToRevealPrivateCredential, rightAnswerIcon, styles.rightText]); + }, [ + goToRevealPrivateCredential, + rightAnswerIcon, + styles.rightText, + trackEvent, + ]); const wrongAnswerQuestionTwo = useCallback((): Element => { - AnalyticsV2.trackEvent( - MetaMetricsEvents.SRP_REVEAL_SECOND_QUESTION_WRONG_ANSWER, - {}, - ); + trackEvent(MetaMetricsEvents.SRP_REVEAL_SECOND_QUESTION_WRONG_ANSWER, {}); return ( { dismiss={dismissModal} /> ); - }, [styles.wrongText, wrongAnswerIcon]); + }, [styles.wrongText, wrongAnswerIcon, trackEvent]); const quizPage = useCallback(() => { switch (stage) { diff --git a/app/util/metrics/TrackOnboarding/trackOnboarding.test.ts b/app/util/metrics/TrackOnboarding/trackOnboarding.test.ts new file mode 100644 index 00000000000..7687982a51d --- /dev/null +++ b/app/util/metrics/TrackOnboarding/trackOnboarding.test.ts @@ -0,0 +1,65 @@ +import trackOnboarding from './trackOnboarding'; + +const { InteractionManager } = jest.requireActual('react-native'); +InteractionManager.runAfterInteractions = jest.fn(async (callback) => + callback(), +); + +const mockEnabled = jest.fn(); +const mockTrackEvent = jest.fn(); + +jest.mock('../../../core/Analytics', () => ({ + MetaMetrics: { + getInstance: jest.fn().mockImplementation(() => ({ + isEnabled: mockEnabled, + trackEvent: mockTrackEvent, + })), + }, +})); + +const mockSaveOnboardingEvent = jest.fn(); + +describe('trackAfterInteractions', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('calls saveOnboardingEvent when metrics is not enabled', async () => { + const mockEvent = { category: 'testEvent' }; + const mockProperties = { prop: 'testProp' }; + + mockEnabled.mockReturnValue(false); + + trackOnboarding(mockEvent, mockProperties, mockSaveOnboardingEvent); + + expect(mockSaveOnboardingEvent).toHaveBeenCalledWith(mockEvent); + expect(mockTrackEvent).not.toHaveBeenCalled(); + }); + + it('call trackEvent when metrics is not enabled but saveOnboardingEvent is not defined', async () => { + const mockEvent = { category: 'testEvent' }; + const mockProperties = { prop: 'testProp' }; + + mockEnabled.mockReturnValue(false); + + trackOnboarding(mockEvent, mockProperties); + + expect(mockSaveOnboardingEvent).not.toHaveBeenCalledWith(); + expect(mockTrackEvent).toHaveBeenCalledWith(mockEvent, mockProperties); + }); + + it('call trackEvent when metrics is enabled', async () => { + const mockEvent = { category: 'testEvent' }; + const mockProperties = { prop: 'testProp' }; + + mockEnabled.mockReturnValue(true); + + trackOnboarding(mockEvent, mockProperties); + trackOnboarding(mockEvent); + + expect(mockSaveOnboardingEvent).not.toHaveBeenCalled(); + expect(mockTrackEvent).toHaveBeenCalledTimes(2); + expect(mockTrackEvent).toHaveBeenCalledWith(mockEvent, mockProperties); + expect(mockTrackEvent).toHaveBeenCalledWith(mockEvent, {}); + }); +}); diff --git a/app/util/metrics/TrackOnboarding/trackOnboarding.ts b/app/util/metrics/TrackOnboarding/trackOnboarding.ts new file mode 100644 index 00000000000..38b2d2920ed --- /dev/null +++ b/app/util/metrics/TrackOnboarding/trackOnboarding.ts @@ -0,0 +1,31 @@ +import { IMetaMetricsEvent, MetaMetrics } from '../../../core/Analytics'; +import { InteractionManager } from 'react-native'; +import { JsonMap } from '@segment/analytics-react-native'; + +/** + * track onboarding event or save it for when metrics are enabled + * @param event - the event to track + * @param properties - the properties map for the event + * @param saveOnboardingEvent - function to store onboarding event before optin + */ +const trackOnboarding = ( + event: IMetaMetricsEvent, + properties: JsonMap = {}, + saveOnboardingEvent?: (event: IMetaMetricsEvent) => { + event: IMetaMetricsEvent; + type: string; + }, +): void => { + InteractionManager.runAfterInteractions(async () => { + const metrics = MetaMetrics.getInstance(); + const isOnboardingDelayedEvent = + !metrics.isEnabled() && saveOnboardingEvent; + if (isOnboardingDelayedEvent) { + saveOnboardingEvent(event); + } else { + metrics.trackEvent(event, properties); + } + }); +}; + +export default trackOnboarding;