Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Approvals and Nav events migration #8658

Merged
merged 6 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -58,9 +58,24 @@ const mockSelectorState = (state: any) => {
);
};

const mockTrackEvent = jest.fn();

describe('PermissionApproval', () => {
beforeEach(() => {
jest.resetAllMocks();
(useMetrics as jest.MockedFn<typeof useMetrics>).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 () => {
Expand Down Expand Up @@ -116,8 +131,8 @@ describe('PermissionApproval', () => {

render(<PermissionApproval navigation={navigationMock} />);

expect(AnalyticsV2.trackEvent).toHaveBeenCalledTimes(1);
expect(AnalyticsV2.trackEvent).toHaveBeenCalledWith(
expect(mockTrackEvent).toHaveBeenCalledTimes(1);
expect(mockTrackEvent).toHaveBeenCalledWith(
MetaMetricsEvents.CONNECT_REQUEST_STARTED,
{
number_of_accounts: 3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean>(false);
Expand All @@ -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',
});
Expand All @@ -46,7 +47,7 @@ const PermissionApproval = (props: PermissionApprovalProps) => {
permissionRequestId: id,
}),
);
}, [approvalRequest, totalAccounts, props.navigation]);
}, [approvalRequest, totalAccounts, props.navigation, trackEvent]);

return null;
};
Expand Down
55 changes: 34 additions & 21 deletions app/components/Nav/App/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -277,10 +278,14 @@ const App = ({ userLoggedIn }) => {
);
}
await Authentication.lockApp(false);
trackErrorAsAnalytics(
'App: Max Attempts Reached',
error?.message,
`Unlock attempts: 1`,
trackEvent(
{ category: 'Error occurred' },
{
error: true,
type: 'App: Max Attempts Reached',
errorMessage: error?.message,
otherInfo: `Unlock attempts: 1`,
},
tommasini marked this conversation as resolved.
Show resolved Hide resolved
);
} finally {
animationRef?.current?.play();
Expand All @@ -296,23 +301,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
Expand Down
13 changes: 6 additions & 7 deletions app/components/Nav/Main/MainNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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();
Expand Down Expand Up @@ -326,6 +326,7 @@ const SettingsFlow = () => (
);

const HomeTabs = () => {
const { trackEvent } = useMetrics();
const drawerRef = useRef(null);
const [isKeyboardHidden, setIsKeyboardHidden] = useState(true);

Expand Down Expand Up @@ -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),
});
Expand All @@ -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',
Expand All @@ -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,
Expand Down
46 changes: 23 additions & 23 deletions app/components/Nav/Main/RootRPCMethodsUI.js
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -64,13 +62,16 @@ 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

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;
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
Loading