From 79ceed1326a28519eac7dfb630bc0cb6350d1ca9 Mon Sep 17 00:00:00 2001 From: tommasini Date: Wed, 21 Feb 2024 17:32:34 +0000 Subject: [PATCH 1/5] Approvals events migration, only event is on PermissionApproval --- .../Approvals/PermissionApproval/PermissionApproval.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/components/Approvals/PermissionApproval/PermissionApproval.tsx b/app/components/Approvals/PermissionApproval/PermissionApproval.tsx index f93fb7b230a..4a12bdae5d8 100644 --- a/app/components/Approvals/PermissionApproval/PermissionApproval.tsx +++ b/app/components/Approvals/PermissionApproval/PermissionApproval.tsx @@ -2,17 +2,18 @@ import { useEffect, useRef } from 'react'; import useApprovalRequest from '../../Views/confirmations/hooks/useApprovalRequest'; import { ApprovalTypes } from '../../../core/RPCMethods/RPCMethodMiddleware'; -import AnalyticsV2 from '../../../util/analyticsV2'; import { MetaMetricsEvents } from '../../../core/Analytics'; import { createAccountConnectNavDetails } from '../../Views/AccountConnect'; import { useSelector } from 'react-redux'; import { selectAccountsLength } from '../../../selectors/accountTrackerController'; +import { useMetrics } from '../../../components/hooks/useMetrics'; export interface PermissionApprovalProps { navigation: any; } const PermissionApproval = (props: PermissionApprovalProps) => { + const { trackEvent } = useMetrics(); const { approvalRequest } = useApprovalRequest(); const totalAccounts = useSelector(selectAccountsLength); const isProcessing = useRef(false); @@ -35,7 +36,7 @@ const PermissionApproval = (props: PermissionApprovalProps) => { isProcessing.current = true; - AnalyticsV2.trackEvent(MetaMetricsEvents.CONNECT_REQUEST_STARTED, { + trackEvent(MetaMetricsEvents.CONNECT_REQUEST_STARTED, { number_of_accounts: totalAccounts, source: 'PERMISSION SYSTEM', }); @@ -46,7 +47,7 @@ const PermissionApproval = (props: PermissionApprovalProps) => { permissionRequestId: id, }), ); - }, [approvalRequest, totalAccounts, props.navigation]); + }, [approvalRequest, totalAccounts, props.navigation, trackEvent]); return null; }; From ec9243f855a3e2c2ebafa5d2b4baf7453325404d Mon Sep 17 00:00:00 2001 From: tommasini Date: Wed, 21 Feb 2024 18:03:12 +0000 Subject: [PATCH 2/5] Nav folder events migration --- app/components/Nav/App/index.js | 54 +++++++++++++-------- app/components/Nav/Main/MainNavigator.js | 13 +++-- app/components/Nav/Main/RootRPCMethodsUI.js | 46 +++++++++--------- 3 files changed, 62 insertions(+), 51 deletions(-) diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index 68697527ce9..0f7ff3d4763 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -30,7 +30,6 @@ import Engine from '../../../core/Engine'; import branch from 'react-native-branch'; import AppConstants from '../../../core/AppConstants'; import Logger from '../../../util/Logger'; -import { trackErrorAsAnalytics } from '../../../util/analyticsV2'; import { routingInstrumentation } from '../../../util/sentry/utils'; import Analytics from '../../../core/Analytics/Analytics'; import { connect, useDispatch } from 'react-redux'; @@ -101,6 +100,7 @@ import ShowIpfsGatewaySheet from '../../Views/ShowIpfsGatewaySheet/ShowIpfsGatew import ShowDisplayNftMediaSheet from '../../Views/ShowDisplayMediaNFTSheet/ShowDisplayNFTMediaSheet'; import AmbiguousAddressSheet from '../../../../app/components/Views/Settings/Contacts/AmbiguousAddressSheet/AmbiguousAddressSheet'; import { MetaMetrics } from '../../../core/Analytics'; +import { useMetrics } from '../../../components/hooks/useMetrics'; const clearStackNavigatorOptions = { headerShown: false, @@ -239,6 +239,7 @@ const App = ({ userLoggedIn }) => { const queueOfHandleDeeplinkFunctions = useRef([]); const [animationPlayed, setAnimationPlayed] = useState(false); const { colors } = useTheme(); + const { trackEvent } = useMetrics(); const { toastRef } = useContext(ToastContext); const dispatch = useDispatch(); const sdkInit = useRef(false); @@ -277,10 +278,13 @@ const App = ({ userLoggedIn }) => { ); } await Authentication.lockApp(false); - trackErrorAsAnalytics( - 'App: Max Attempts Reached', - error?.message, - `Unlock attempts: 1`, + trackEvent( + { category: 'Error occurred' }, + { + type: 'App: Max Attempts Reached', + errorMessage: error?.message, + otherInfo: `Unlock attempts: 1`, + }, ); } finally { animationRef?.current?.play(); @@ -296,23 +300,31 @@ const App = ({ userLoggedIn }) => { .catch((error) => { Logger.error(error, 'App: Error in appTriggeredAuth'); }); - }, [navigator, queueOfHandleDeeplinkFunctions]); - - const handleDeeplink = useCallback(({ error, params, uri }) => { - if (error) { - trackErrorAsAnalytics(error, 'Branch:'); - } - const deeplink = params?.['+non_branch_link'] || uri || null; - try { - if (deeplink) { - SharedDeeplinkManager.parse(deeplink, { - origin: AppConstants.DEEPLINKS.ORIGIN_DEEPLINK, - }); + }, [navigator, queueOfHandleDeeplinkFunctions, trackEvent]); + + const handleDeeplink = useCallback( + ({ error, params, uri }) => { + if (error) { + trackEvent( + { category: 'Error occurred' }, + { + errorMessage: 'Branch:', + }, + ); } - } catch (e) { - Logger.error(e, `Deeplink: Error parsing deeplink`); - } - }, []); + const deeplink = params?.['+non_branch_link'] || uri || null; + try { + if (deeplink) { + SharedDeeplinkManager.parse(deeplink, { + origin: AppConstants.DEEPLINKS.ORIGIN_DEEPLINK, + }); + } + } catch (e) { + Logger.error(e, `Deeplink: Error parsing deeplink`); + } + }, + [trackEvent], + ); // on Android devices, this creates a listener // to deeplinks used to open the app diff --git a/app/components/Nav/Main/MainNavigator.js b/app/components/Nav/Main/MainNavigator.js index 34b10a8f555..eb9f1dff629 100644 --- a/app/components/Nav/Main/MainNavigator.js +++ b/app/components/Nav/Main/MainNavigator.js @@ -61,7 +61,6 @@ import { SnapsSettingsList } from '../../Views/Snaps/SnapsSettingsList'; import { SnapSettings } from '../../Views/Snaps/SnapSettings'; ///: END:ONLY_INCLUDE_IF import Routes from '../../../constants/navigation/Routes'; -import AnalyticsV2 from '../../../util/analyticsV2'; import { MetaMetricsEvents } from '../../../core/Analytics'; import { getActiveTabUrl } from '../../../util/transactions'; import { getPermittedAccountsByHostname } from '../../../core/Permissions'; @@ -74,6 +73,7 @@ import SDKSessionsManager from '../../Views/SDKSessionsManager/SDKSessionsManage import URL from 'url-parse'; import Logger from '../../../util/Logger'; import { getDecimalChainId } from '../../../util/networks'; +import { useMetrics } from '../../../components/hooks/useMetrics'; const Stack = createStackNavigator(); const Tab = createBottomTabNavigator(); @@ -326,6 +326,7 @@ const SettingsFlow = () => ( ); const HomeTabs = () => { + const { trackEvent } = useMetrics(); const drawerRef = useRef(null); const [isKeyboardHidden, setIsKeyboardHidden] = useState(true); @@ -365,7 +366,7 @@ const HomeTabs = () => { home: { tabBarIconKey: TabBarIconKey.Wallet, callback: () => { - AnalyticsV2.trackEvent(MetaMetricsEvents.WALLET_OPENED, { + trackEvent(MetaMetricsEvents.WALLET_OPENED, { number_of_accounts: accountsLength, chain_id: getDecimalChainId(chainId), }); @@ -379,7 +380,7 @@ const HomeTabs = () => { browser: { tabBarIconKey: TabBarIconKey.Browser, callback: () => { - AnalyticsV2.trackEvent(MetaMetricsEvents.BROWSER_OPENED, { + trackEvent(MetaMetricsEvents.BROWSER_OPENED, { number_of_accounts: accountsLength, chain_id: getDecimalChainId(chainId), source: 'Navigation Tab', @@ -392,16 +393,14 @@ const HomeTabs = () => { activity: { tabBarIconKey: TabBarIconKey.Activity, callback: () => { - AnalyticsV2.trackEvent( - MetaMetricsEvents.NAVIGATION_TAPS_TRANSACTION_HISTORY, - ); + trackEvent(MetaMetricsEvents.NAVIGATION_TAPS_TRANSACTION_HISTORY); }, rootScreenName: Routes.TRANSACTIONS_VIEW, }, settings: { tabBarIconKey: TabBarIconKey.Setting, callback: () => { - AnalyticsV2.trackEvent(MetaMetricsEvents.NAVIGATION_TAPS_SETTINGS); + trackEvent(MetaMetricsEvents.NAVIGATION_TAPS_SETTINGS); }, rootScreenName: Routes.SETTINGS_VIEW, unmountOnBlur: true, diff --git a/app/components/Nav/Main/RootRPCMethodsUI.js b/app/components/Nav/Main/RootRPCMethodsUI.js index fbad281c670..ca6007d86bc 100644 --- a/app/components/Nav/Main/RootRPCMethodsUI.js +++ b/app/components/Nav/Main/RootRPCMethodsUI.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useCallback } from 'react'; -import { Alert, InteractionManager } from 'react-native'; +import { Alert } from 'react-native'; import PropTypes from 'prop-types'; import { connect, useSelector } from 'react-redux'; import { ethers } from 'ethers'; @@ -30,12 +30,10 @@ import Logger from '../../../util/Logger'; import TransactionTypes from '../../../core/TransactionTypes'; import { swapsUtils } from '@metamask/swaps-controller'; import { query } from '@metamask/controller-utils'; -import Analytics from '../../../core/Analytics/Analytics'; import BigNumber from 'bignumber.js'; import { toLowerCaseEquals } from '../../../util/general'; import { KEYSTONE_TX_CANCELED } from '../../../constants/error'; import { MetaMetricsEvents } from '../../../core/Analytics'; -import AnalyticsV2 from '../../../util/analyticsV2'; import { getAddressAccountType, isHardwareAccount, @@ -64,6 +62,8 @@ import { selectSelectedAddress } from '../../../selectors/preferencesController' import { getLedgerKeyring } from '../../../core/Ledger/Ledger'; import { createLedgerTransactionModalNavDetails } from '../../UI/LedgerModals/LedgerTransactionModal'; import ExtendedKeyringTypes from '../../../constants/keyringTypes'; +import { useMetrics } from '../../../components/hooks/useMetrics'; + ///: BEGIN:ONLY_INCLUDE_IF(snaps) import InstallSnapApproval from '../../Approvals/InstallSnapApproval'; ///: END:ONLY_INCLUDE_IF @@ -71,6 +71,7 @@ import InstallSnapApproval from '../../Approvals/InstallSnapApproval'; const hstInterface = new ethers.utils.Interface(abi); const RootRPCMethodsUI = (props) => { + const { trackEvent, trackAnonymousEvent } = useMetrics(); const [transactionModalType, setTransactionModalType] = useState(undefined); const tokenList = useSelector(selectTokenList); const setTransactionObject = props.setTransactionObject; @@ -165,27 +166,28 @@ const RootRPCMethodsUI = (props) => { delete newSwapsTransactions[transactionMeta.id].analytics; delete newSwapsTransactions[transactionMeta.id].paramsForAnalytics; - InteractionManager.runAfterInteractions(() => { - const parameters = { - ...analyticsParams, - time_to_mine: timeToMine, - estimated_vs_used_gasRatio: estimatedVsUsedGasRatio, - quote_vs_executionRatio: quoteVsExecutionRatio, - token_to_amount_received: tokenToAmountReceived.toString(), - }; - Analytics.trackEventWithParameters(event, {}); - Analytics.trackEventWithParameters(event, parameters, true); - }); + const parameters = { + ...analyticsParams, + time_to_mine: timeToMine, + estimated_vs_used_gasRatio: estimatedVsUsedGasRatio, + quote_vs_executionRatio: quoteVsExecutionRatio, + token_to_amount_received: tokenToAmountReceived.toString(), + }; + + trackAnonymousEvent(event, parameters); } catch (e) { Logger.error(e, MetaMetricsEvents.SWAP_TRACKING_FAILED); - InteractionManager.runAfterInteractions(() => { - Analytics.trackEvent(MetaMetricsEvents.SWAP_TRACKING_FAILED, { - error: e, - }); + trackEvent(MetaMetricsEvents.SWAP_TRACKING_FAILED, { + error: e, }); } }, - [props.selectedAddress, props.swapsTransactions], + [ + props.selectedAddress, + props.swapsTransactions, + trackEvent, + trackAnonymousEvent, + ], ); const autoSign = useCallback( @@ -248,13 +250,11 @@ const RootRPCMethodsUI = (props) => { ); Logger.error(error, 'error while trying to send transaction (Main)'); } else { - AnalyticsV2.trackEvent( - MetaMetricsEvents.QR_HARDWARE_TRANSACTION_CANCELED, - ); + trackEvent(MetaMetricsEvents.QR_HARDWARE_TRANSACTION_CANCELED); } } }, - [props.navigation, props.swapsTransactions, trackSwaps], + [props.navigation, props.swapsTransactions, trackSwaps, trackEvent], ); const onUnapprovedTransaction = useCallback( From 2733f08cb3de3aea23bb46aa43a60b85e25542b9 Mon Sep 17 00:00:00 2001 From: tommasini Date: Wed, 21 Feb 2024 19:19:22 +0000 Subject: [PATCH 3/5] track event error fixed on nav/app --- app/components/Nav/App/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index 0f7ff3d4763..ff7f129dbf5 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -281,6 +281,7 @@ const App = ({ userLoggedIn }) => { trackEvent( { category: 'Error occurred' }, { + error: true, type: 'App: Max Attempts Reached', errorMessage: error?.message, otherInfo: `Unlock attempts: 1`, From f3dfba5a8af4e2d57551777034013b2980f3d268 Mon Sep 17 00:00:00 2001 From: tommasini Date: Wed, 21 Feb 2024 23:19:33 +0000 Subject: [PATCH 4/5] mock Track event --- .../PermissionApproval.test.tsx | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/app/components/Approvals/PermissionApproval/PermissionApproval.test.tsx b/app/components/Approvals/PermissionApproval/PermissionApproval.test.tsx index c43c389ff5b..86ad746e141 100644 --- a/app/components/Approvals/PermissionApproval/PermissionApproval.test.tsx +++ b/app/components/Approvals/PermissionApproval/PermissionApproval.test.tsx @@ -4,14 +4,14 @@ import { ApprovalTypes } from '../../../core/RPCMethods/RPCMethodMiddleware'; import { ApprovalRequest } from '@metamask/approval-controller'; import PermissionApproval from './PermissionApproval'; import { createAccountConnectNavDetails } from '../../Views/AccountConnect'; -import AnalyticsV2 from '../../../util/analyticsV2'; import { useSelector } from 'react-redux'; import { MetaMetricsEvents } from '../../../core/Analytics'; import initialBackgroundState from '../../../util/test/initial-background-state.json'; import { render } from '@testing-library/react-native'; +import { useMetrics } from '../../../components/hooks/useMetrics'; jest.mock('../../Views/confirmations/hooks/useApprovalRequest'); -jest.mock('../../../util/analyticsV2'); +jest.mock('../../../components/hooks/useMetrics'); jest.mock('../../Views/AccountConnect', () => ({ createAccountConnectNavDetails: jest.fn(), @@ -58,9 +58,24 @@ const mockSelectorState = (state: any) => { ); }; +const mockTrackEvent = jest.fn(); + describe('PermissionApproval', () => { beforeEach(() => { jest.resetAllMocks(); + (useMetrics as jest.MockedFn).mockReturnValue({ + trackEvent: mockTrackEvent, + trackAnonymousEvent: jest.fn(), + enable: jest.fn(), + addTraitsToUser: jest.fn(), + createDataDeletionTask: jest.fn(), + checkDataDeleteStatus: jest.fn(), + getDeleteRegulationCreationDate: jest.fn(), + getDeleteRegulationId: jest.fn(), + isDataRecorded: jest.fn(), + isEnabled: jest.fn(), + getMetaMetricsId: jest.fn(), + }); }); it('navigates', async () => { @@ -116,8 +131,8 @@ describe('PermissionApproval', () => { render(); - expect(AnalyticsV2.trackEvent).toHaveBeenCalledTimes(1); - expect(AnalyticsV2.trackEvent).toHaveBeenCalledWith( + expect(mockTrackEvent).toHaveBeenCalledTimes(1); + expect(mockTrackEvent).toHaveBeenCalledWith( MetaMetricsEvents.CONNECT_REQUEST_STARTED, { number_of_accounts: 3, From 879edc80d6c480cf08738acb80bd8986796e04de Mon Sep 17 00:00:00 2001 From: tommasini Date: Thu, 22 Feb 2024 16:23:22 +0000 Subject: [PATCH 5/5] change track event to the new trackErrorAsANalytics --- app/components/Nav/App/index.js | 55 +++++++++++++-------------------- 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index ff7f129dbf5..109d8c63a72 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -100,7 +100,7 @@ import ShowIpfsGatewaySheet from '../../Views/ShowIpfsGatewaySheet/ShowIpfsGatew import ShowDisplayNftMediaSheet from '../../Views/ShowDisplayMediaNFTSheet/ShowDisplayNFTMediaSheet'; import AmbiguousAddressSheet from '../../../../app/components/Views/Settings/Contacts/AmbiguousAddressSheet/AmbiguousAddressSheet'; import { MetaMetrics } from '../../../core/Analytics'; -import { useMetrics } from '../../../components/hooks/useMetrics'; +import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics'; const clearStackNavigatorOptions = { headerShown: false, @@ -239,7 +239,6 @@ const App = ({ userLoggedIn }) => { const queueOfHandleDeeplinkFunctions = useRef([]); const [animationPlayed, setAnimationPlayed] = useState(false); const { colors } = useTheme(); - const { trackEvent } = useMetrics(); const { toastRef } = useContext(ToastContext); const dispatch = useDispatch(); const sdkInit = useRef(false); @@ -278,14 +277,10 @@ const App = ({ userLoggedIn }) => { ); } await Authentication.lockApp(false); - trackEvent( - { category: 'Error occurred' }, - { - error: true, - type: 'App: Max Attempts Reached', - errorMessage: error?.message, - otherInfo: `Unlock attempts: 1`, - }, + trackErrorAsAnalytics( + 'App: Max Attempts Reached', + error?.message, + `Unlock attempts: 1`, ); } finally { animationRef?.current?.play(); @@ -301,31 +296,23 @@ const App = ({ userLoggedIn }) => { .catch((error) => { Logger.error(error, 'App: Error in appTriggeredAuth'); }); - }, [navigator, queueOfHandleDeeplinkFunctions, trackEvent]); - - const handleDeeplink = useCallback( - ({ error, params, uri }) => { - if (error) { - trackEvent( - { category: 'Error occurred' }, - { - errorMessage: 'Branch:', - }, - ); - } - const deeplink = params?.['+non_branch_link'] || uri || null; - try { - if (deeplink) { - SharedDeeplinkManager.parse(deeplink, { - origin: AppConstants.DEEPLINKS.ORIGIN_DEEPLINK, - }); - } - } catch (e) { - Logger.error(e, `Deeplink: Error parsing deeplink`); + }, [navigator, queueOfHandleDeeplinkFunctions]); + + const handleDeeplink = useCallback(({ error, params, uri }) => { + if (error) { + trackErrorAsAnalytics(error, 'Branch:'); + } + const deeplink = params?.['+non_branch_link'] || uri || null; + try { + if (deeplink) { + SharedDeeplinkManager.parse(deeplink, { + origin: AppConstants.DEEPLINKS.ORIGIN_DEEPLINK, + }); } - }, - [trackEvent], - ); + } catch (e) { + Logger.error(e, `Deeplink: Error parsing deeplink`); + } + }, []); // on Android devices, this creates a listener // to deeplinks used to open the app