From 8be3edc33da4d71a78e03d74f1c3c991bef9d8d0 Mon Sep 17 00:00:00 2001 From: Nicolas MASSART Date: Mon, 27 Jan 2025 18:11:50 +0100 Subject: [PATCH 1/3] move ident to state listener --- app/components/Nav/App/index.js | 10 +--------- app/core/AppStateEventListener.ts | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index c95d5205c9c..95bda62dd62 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -745,15 +745,7 @@ const App = (props) => { useEffect(() => { const initMetrics = async () => { - const metrics = MetaMetrics.getInstance(); - await metrics.configure(); - // identify user with the latest traits - // run only after the MetaMetrics is configured - const consolidatedTraits = { - ...generateDeviceAnalyticsMetaData(), - ...generateUserSettingsAnalyticsMetaData(), - }; - await metrics.addTraitsToUser(consolidatedTraits); + await MetaMetrics.getInstance().configure(); }; initMetrics().catch((err) => { diff --git a/app/core/AppStateEventListener.ts b/app/core/AppStateEventListener.ts index f6be9afc39c..e9c7e0e79a9 100644 --- a/app/core/AppStateEventListener.ts +++ b/app/core/AppStateEventListener.ts @@ -5,6 +5,9 @@ import { MetricsEventBuilder } from './Analytics/MetricsEventBuilder'; import { processAttribution } from './processAttribution'; import DevLogger from './SDKConnect/utils/DevLogger'; import ReduxService from './redux'; +import generateDeviceAnalyticsMetaData from '../util/metrics'; +import generateUserSettingsAnalyticsMetaData + from '../util/metrics/UserSettingsAnalyticsMetaData/generateUserProfileAnalyticsMetaData'; export class AppStateEventListener { private appStateSubscription: @@ -48,6 +51,18 @@ export class AppStateEventListener { currentDeeplink: this.currentDeeplink, store: ReduxService.store, }); + const metrics = MetaMetrics.getInstance(); + // identify user with the latest traits + const consolidatedTraits = { + ...generateDeviceAnalyticsMetaData(), + ...generateUserSettingsAnalyticsMetaData(), + }; + metrics.addTraitsToUser(consolidatedTraits).catch((error) => { + Logger.error( + error as Error, + 'AppStateManager: Error adding traits to user', + ); + }); const appOpenedEventBuilder = MetricsEventBuilder.createEventBuilder(MetaMetricsEvents.APP_OPENED); if (attribution) { const { attributionId, utm, ...utmParams } = attribution; @@ -57,7 +72,7 @@ export class AppStateEventListener { ); appOpenedEventBuilder.addProperties({ attributionId, ...utmParams }); } - MetaMetrics.getInstance().trackEvent(appOpenedEventBuilder.build()); + metrics.trackEvent(appOpenedEventBuilder.build()); } catch (error) { Logger.error( error as Error, From 6def04d225b95ac13a1282621d3d66c9f87c8be7 Mon Sep 17 00:00:00 2001 From: Nicolas MASSART Date: Mon, 27 Jan 2025 18:43:10 +0100 Subject: [PATCH 2/3] update unit tests --- app/components/Nav/App/index.test.tsx | 4 --- app/core/AppStateEventListener.test.ts | 45 +++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/app/components/Nav/App/index.test.tsx b/app/components/Nav/App/index.test.tsx index 68faee0918a..6271239052e 100644 --- a/app/components/Nav/App/index.test.tsx +++ b/app/components/Nav/App/index.test.tsx @@ -61,10 +61,6 @@ describe('App', () => { ); await waitFor(() => { expect(mockMetrics.configure).toHaveBeenCalledTimes(1); - expect(mockMetrics.addTraitsToUser).toHaveBeenNthCalledWith(1, { - deviceProp: 'Device value', - userProp: 'User value', - }); }); }); }); diff --git a/app/core/AppStateEventListener.test.ts b/app/core/AppStateEventListener.test.ts index 02606e78299..860b8d70a00 100644 --- a/app/core/AppStateEventListener.test.ts +++ b/app/core/AppStateEventListener.test.ts @@ -19,7 +19,7 @@ jest.mock('./processAttribution', () => ({ jest.mock('./Analytics/MetaMetrics'); const mockMetrics = { - trackEvent: jest.fn().mockImplementation(() => Promise.resolve()), + trackEvent: jest.fn(), enable: jest.fn(() => Promise.resolve()), addTraitsToUser: jest.fn(() => Promise.resolve()), isEnabled: jest.fn(() => true), @@ -105,6 +105,49 @@ describe('AppStateEventListener', () => { ); }); + it('identifies user when app becomes active', () => { + jest + .spyOn(ReduxService, 'store', 'get') + .mockReturnValue({} as unknown as ReduxStore); + + mockAppStateListener('active'); + jest.advanceTimersByTime(2000); + + expect(mockMetrics.addTraitsToUser).toHaveBeenCalledTimes(1); + expect(mockMetrics.addTraitsToUser).toHaveBeenCalledWith({ + 'Batch account balance requests': 'OFF', + 'Enable OpenSea API': 'OFF', + 'NFT Autodetection': 'OFF', + 'Theme': undefined, + 'applicationVersion': expect.any(Promise), + 'currentBuildNumber': expect.any(Promise), + 'deviceBrand': 'Apple', + 'operatingSystemVersion': 'ios', + 'platform': 'ios', + 'security_providers': '', + 'token_detection_enable': 'OFF', + }); + }); + + it('logs error when identifying user fails', () => { + jest + .spyOn(ReduxService, 'store', 'get') + .mockReturnValue({} as unknown as ReduxStore); + const testError = new Error('Test error'); + mockMetrics.addTraitsToUser.mockImplementation(() => { + throw testError; + }); + + mockAppStateListener('active'); + jest.advanceTimersByTime(2000); + + expect(Logger.error).toHaveBeenCalledWith( + testError, + 'AppStateManager: Error processing app state change' + ); + expect(mockMetrics.trackEvent).not.toHaveBeenCalled(); + }); + it('handles errors gracefully', () => { jest .spyOn(ReduxService, 'store', 'get') From e649aeff507dd28572b0dd1c4a5b5511e602ec85 Mon Sep 17 00:00:00 2001 From: Nicolas MASSART Date: Mon, 27 Jan 2025 19:01:53 +0100 Subject: [PATCH 3/3] clean import --- app/components/Nav/App/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index 95bda62dd62..f6b50e6f1c5 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -109,8 +109,6 @@ import SDKSessionModal from '../../Views/SDK/SDKSessionModal/SDKSessionModal'; import ExperienceEnhancerModal from '../../../../app/components/Views/ExperienceEnhancerModal'; import { MetaMetrics } from '../../../core/Analytics'; import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics'; -import generateDeviceAnalyticsMetaData from '../../../util/metrics/DeviceAnalyticsMetaData/generateDeviceAnalyticsMetaData'; -import generateUserSettingsAnalyticsMetaData from '../../../util/metrics/UserSettingsAnalyticsMetaData/generateUserProfileAnalyticsMetaData'; import LedgerSelectAccount from '../../Views/LedgerSelectAccount'; import OnboardingSuccess from '../../Views/OnboardingSuccess'; import DefaultSettings from '../../Views/OnboardingSuccess/DefaultSettings';