refactor(suite-native): remove legacy analytics and update to new analytics service across multiple components

This commit is contained in:
Jan Václavík
2026-02-05 06:32:50 +01:00
parent 64ea548f16
commit e81194535a
29 changed files with 20 additions and 163 deletions

View File

@@ -77,8 +77,6 @@ export const init = () => (dispatch: Dispatch, getState: GetState, extra: ExtraD
},
});
// `sendReport` argument is used only for not sending event `EventType.SettingsAnalytics` twice and it will not be necessary since we delete legacyAnalytics (https://github.com/trezor/trezor-suite/issues/24428)
extra.services.legacyAnalytics.init(hasUserAllowedTracking, getOptions({ sendReport: false }));
extra.services.analytics.init(hasUserAllowedTracking, getOptions({ sendReport: true }));
allowSentryReport(isAnalyticsEnabled);

View File

@@ -3,9 +3,7 @@ import { saveAs } from 'file-saver';
import {
DesktopAnalyticsDep,
DesktopLegacyAnalyticsDep,
createAnalytics,
createLegacyAnalytics,
} from '@suite/analytics';
import { createElectronPlatformEncryption } from '@suite/platform-encryption-electron';
import { createWebauthnPlatformEncryption } from '@suite/platform-encryption-webauthn';
@@ -109,7 +107,6 @@ export type SuiteAppDeps = StoreAPIDep & HistoryDep & SuiteSyncAppReloaderDep;
export type SuiteServices = CommonServices &
DesktopAnalyticsDep &
DesktopLegacyAnalyticsDep &
DisableLegacyMetadataIfNeededDep &
SuiteRouterHistoryDep;
@@ -131,7 +128,6 @@ export const createSuiteServicesCompositionRoot = (deps: SuiteAppDeps): SuiteSer
getState: deps.getState,
});
const legacyAnalytics = createLegacyAnalytics();
const analytics = createAnalytics();
const suiteSync = createSuiteSyncDesktopCompositionRoot({
@@ -148,8 +144,7 @@ export const createSuiteServicesCompositionRoot = (deps: SuiteAppDeps): SuiteSer
return {
suiteSync,
platformEncryption,
legacyAnalytics,
analytics: createAnalytics(),
analytics,
disableLegacyMetadataIfNeeded,
suiteRouterHistory: createSuiteRouterHistory({
history: deps.history,

View File

@@ -1,9 +1,5 @@
import { ExtraDependenciesStatic } from '@suite-common/redux-utils';
import {
analyticsMock,
extraDependenciesCommonMock,
legacyAnalyticsMock,
} from '@suite-common/test-utils';
import { analyticsMock, extraDependenciesCommonMock } from '@suite-common/test-utils';
import { SuiteServices } from '../extraDependencies';
@@ -14,7 +10,6 @@ export const extraDependenciesDesktopMock: ExtraDependenciesSuiteMock = {
services: {
...extraDependenciesCommonMock.services,
analytics: analyticsMock, // To satisfy Suite specific type as ExtraDependenciesStatic tightness it
legacyAnalytics: legacyAnalyticsMock, // To satisfy Suite specific type as ExtraDependenciesStatic tightness it
suiteRouterHistory: {
getLocation: () => ({
pathname: '/mocked_path',

View File

@@ -5,10 +5,3 @@ export const useAnalytics = () => {
return suiteServices['analytics'];
};
/** @deprecated use `useAnalytics` instead */
export const useLegacyAnalytics = () => {
const suiteServices = useSuiteServices();
return suiteServices['legacyAnalytics'];
};

View File

@@ -9,7 +9,7 @@ import { Switch } from '@trezor/components';
import { SettingsSectionItem } from 'src/components/settings/SettingsSectionItem';
import { ActionColumn, TextColumn } from 'src/components/suite';
import { SettingsAnchor } from 'src/constants/suite/anchors';
import { useAnalytics, useLegacyAnalytics } from 'src/support/useAnalytics';
import { useAnalytics } from 'src/support/useAnalytics';
const PositionedSwitch = styled.div`
align-self: center;
@@ -17,7 +17,6 @@ const PositionedSwitch = styled.div`
export const Analytics = () => {
const isAnalyticsEnabled = useSelector(selectIsAnalyticsEnabled);
const legacyAnalytics = useLegacyAnalytics();
const analytics = useAnalytics();
return (
@@ -33,10 +32,8 @@ export const Analytics = () => {
isChecked={isAnalyticsEnabled}
onChange={() => {
if (isAnalyticsEnabled) {
legacyAnalytics.disable();
analytics.disable();
} else {
legacyAnalytics.enable();
analytics.enable();
}
}}

View File

@@ -5,20 +5,17 @@ import { DataAnalytics } from '@trezor/product-components';
import { DATA_TOS_URL, DOCS_ANALYTICS_URL } from '@trezor/urls';
import { TrezorLink } from 'src/components/suite/TrezorLink';
import { useAnalytics, useLegacyAnalytics } from 'src/support/useAnalytics';
import { useAnalytics } from 'src/support/useAnalytics';
import { WelcomeLayoutWithoutModalSwitcher } from '../../components/suite/layouts/WelcomeLayout/WelcomeLayoutWithoutModalSwitcher';
export const AnalyticsConsentScreen = () => {
const legacyAnalytics = useLegacyAnalytics();
const analytics = useAnalytics();
const onConfirm = (trackingEnabled: boolean) => {
if (trackingEnabled) {
legacyAnalytics.enable();
analytics.enable();
} else {
legacyAnalytics.disable();
analytics.disable();
}
};

View File

@@ -39,7 +39,6 @@ export type ConnectInitSettings = {
export type CommonServices = SuiteSyncDep &
PlatformEncryptionDep & {
analytics: Analytics<AnalyticsSharedEvents>;
legacyAnalytics: Analytics<any>;
saveAs: (data: Blob, fileName: string) => void;
connectInitSettings: ConnectInitSettings;
} & ReportSecurityCheckDep &

View File

@@ -56,14 +56,6 @@ export const analyticsMock: Analytics<any> = {
init: () => {},
};
export const legacyAnalyticsMock: Analytics<any> = {
report: () => {},
isEnabled: () => true,
disable: () => {},
enable: () => {},
init: () => {},
};
const connectInitSettings: ConnectInitSettings = {
debug: false,
manifest: {
@@ -84,7 +76,6 @@ export const extraDependenciesCommonMock: ExtraDependencies = {
services: {
suiteSync: suiteSyncMock,
platformEncryption: platformEncryptionMock,
legacyAnalytics: legacyAnalyticsMock,
analytics: analyticsMock,
reportSecurityCheck: ({ level, checkType }: ReportSecurityCheckParams) =>
console.warn(`Mock reporting ${checkType} check ${level} to Sentry.`),

View File

@@ -13,7 +13,6 @@ import { getCommitHash } from '@trezor/env-utils';
import { EventType } from './constants';
import { getTypedNativeAnalytics } from './getTypedNativeAnalytics';
import { getTypedNativeLegacyAnalytics } from './getTypedNativeLegacyAnalytics';
const ACTION_PREFIX = '@suite-native/analytics';
@@ -62,10 +61,6 @@ export const initAnalyticsThunk = createThunk(
},
};
getTypedNativeLegacyAnalytics(extra.services.legacyAnalytics).init(
hasUserAllowedTracking,
options,
);
extra.services.analytics.init(hasUserAllowedTracking, options);
allowSentryReport(isAnalyticsEnabled);

View File

@@ -1,30 +0,0 @@
import { isDebugEnv } from '@suite-native/config';
import { Analytics, Event, QueuedAnalytics } from '@trezor/analytics-uploader';
import { getSuiteVersion } from '@trezor/env-utils';
import { SuiteNativeLegacyAnalyticsEvents } from './types';
/** @deprecated */
export type NativeLegacyAnalyticsDep = {
legacyAnalytics: Analytics<SuiteNativeLegacyAnalyticsEvents>;
};
/** @deprecated use `createAnalytics` instead */
export const createLegacyAnalytics = (): Analytics<SuiteNativeLegacyAnalyticsEvents> => {
const analytics = new QueuedAnalytics<SuiteNativeLegacyAnalyticsEvents>({
version: getSuiteVersion(),
app: 'suite',
});
if (isDebugEnv()) {
// Do not send analytics in development
analytics.report = (event: Event) => {
if (process.env.EXPO_PUBLIC_IS_ANALYTICS_LOGGER_ENABLED === 'true') {
// eslint-disable-next-line no-console
console.log(`Analytics report (legacy) '${event.type}':`, event);
}
};
}
return analytics;
};

View File

@@ -1,7 +0,0 @@
import { Analytics } from '@trezor/analytics-uploader';
import { SuiteNativeLegacyAnalyticsEvents } from './types';
// @TODO: we have to type dispatch correctly in desktop/native/common
export const getTypedNativeLegacyAnalytics = (legacyAnalytics: Analytics<any>) =>
legacyAnalytics as unknown as Analytics<SuiteNativeLegacyAnalyticsEvents>;

View File

@@ -22,11 +22,8 @@ export type {
CountryChangeContextCheck,
CountryChangeContext,
CountryChangeAction,
SuiteNativeLegacyAnalyticsEvents,
} from './types';
export { createAnalytics, type NativeAnalyticsDep } from './createAnalytics';
export { createLegacyAnalytics, type NativeLegacyAnalyticsDep } from './createLegacyAnalytics';
export { getTypedNativeLegacyAnalytics } from './getTypedNativeLegacyAnalytics';
export { getTypedNativeAnalytics } from './getTypedNativeAnalytics';
export type { AnalyticsNativeEvents } from './analyticsEvents';

View File

@@ -3,6 +3,3 @@ import { TradingType } from '@suite-common/trading';
export type CountryChangeContextCheck = 'settings' | 'onboarding';
export type CountryChangeContext = Exclude<TradingType, 'exchange'> | CountryChangeContextCheck;
export type CountryChangeAction = 'submitDefault' | 'submitCustom' | 'cancel';
/** @deprecated use `AnalyticsNativeEvents` */
export type SuiteNativeLegacyAnalyticsEvents = never;

View File

@@ -1,11 +1,7 @@
import { useState } from 'react';
import { AnalyticsSharedEvents } from '@suite-common/analytics';
import {
AnalyticsNativeEvents,
EventType,
SuiteNativeLegacyAnalyticsEvents,
} from '@suite-native/analytics';
import { AnalyticsNativeEvents, EventType } from '@suite-native/analytics';
import {
Box,
Button,
@@ -24,7 +20,7 @@ import {
Screen,
StackProps,
} from '@suite-native/navigation';
import { useAnalytics, useLegacyAnalytics } from '@suite-native/services';
import { useAnalytics } from '@suite-native/services';
import { Analytics } from '@trezor/analytics-uploader';
import { prepareNativeStyle, useNativeStyles } from '@trezor/styles';
import { DATA_PRIVACY_URL } from '@trezor/urls';
@@ -39,14 +35,12 @@ const consentWrapperStyle = prepareNativeStyle(utils => ({
const reportAnalyticsOnboardingCompleted = (
isTrackingAllowed: boolean,
legacyAnalytics: Analytics<SuiteNativeLegacyAnalyticsEvents>,
analytics: Analytics<AnalyticsSharedEvents> & Analytics<AnalyticsNativeEvents>,
) => {
// For users who have not allowed tracking, enable analytics just for reporting
// the OnboardingCompleted event and then disable it again.
if (!isTrackingAllowed) {
analytics.enable();
legacyAnalytics.enable();
}
analytics.report({
type: EventType.OnboardingCompleted,
@@ -55,7 +49,6 @@ const reportAnalyticsOnboardingCompleted = (
if (!isTrackingAllowed) {
analytics.disable();
legacyAnalytics.disable();
}
};
@@ -63,7 +56,6 @@ export const AnalyticsConsentScreen = ({
navigation,
}: StackProps<OnboardingStackParamList, OnboardingStackRoutes.AnalyticsConsent>) => {
const analytics = useAnalytics();
const legacyAnalytics = useLegacyAnalytics();
const [isEnabled, setIsEnabled] = useState(true);
const { applyStyle } = useNativeStyles();
@@ -71,13 +63,12 @@ export const AnalyticsConsentScreen = ({
const handleOpenLink = useOpenLink();
const handleRedirect = () => {
reportAnalyticsOnboardingCompleted(isEnabled, legacyAnalytics, analytics);
reportAnalyticsOnboardingCompleted(isEnabled, analytics);
navigation.navigate(OnboardingStackRoutes.Biometrics);
};
const handleAnalyticsConsent = () => {
legacyAnalytics.enable();
analytics.enable();
handleRedirect();
};

View File

@@ -2,7 +2,7 @@ import { RouteProp } from '@react-navigation/native';
import { EventType } from '@suite-native/analytics';
import { TradingStackParamList, TradingStackRoutes } from '@suite-native/navigation';
import { useAnalytics, useLegacyAnalytics } from '@suite-native/services';
import { useAnalytics } from '@suite-native/services';
import { renderWithStoreProviderAsync, screen, userEvent } from '@suite-native/test-utils';
import { TradingLocationScreen } from '../TradingLocationScreen';
@@ -16,7 +16,6 @@ jest.mock('@suite-native/services', () => {
return {
...original,
useAnalytics: jest.fn(),
useLegacyAnalytics: jest.fn(),
};
});
@@ -43,9 +42,6 @@ describe('TradingLocationOnboardingScreen', () => {
(useAnalytics as jest.Mock).mockReturnValue({
report: reportMock,
});
(useLegacyAnalytics as jest.Mock).mockReturnValue({
report: reportMock,
});
});
afterEach(() => {

View File

@@ -6,13 +6,13 @@ import { selectSelectedDevice } from '@suite-common/wallet-core';
import { useAlert } from '@suite-native/alerts';
import { TouchableSwitchRow } from '@suite-native/atoms';
import { Translation } from '@suite-native/intl';
import { useLegacyAnalytics, useNativeServices } from '@suite-native/services';
import { useAnalytics, useNativeServices } from '@suite-native/services';
import { StorageContext } from '@suite-native/storage';
import { useToast } from '@suite-native/toasts';
import { exhaustive } from '@trezor/type-utils';
export const ToggleSuiteSyncCard = () => {
const analytics = useLegacyAnalytics();
const analytics = useAnalytics();
const persistor = useContext(StorageContext);
const { showAlert } = useAlert();
const { showToast } = useToast();

View File

@@ -14,7 +14,7 @@ import {
import { useBiometricsSettings, useIsBiometricsEnabled } from '@suite-native/biometrics';
import { Translation } from '@suite-native/intl';
import { DynamicScreenHeader, Screen } from '@suite-native/navigation';
import { useAnalytics, useLegacyAnalytics } from '@suite-native/services';
import { useAnalytics } from '@suite-native/services';
import { useNativeStyles } from '@trezor/styles';
const DiscreetTextExample = () => {
@@ -63,18 +63,15 @@ const DiscreetModeSwitchRow = () => {
};
const AnalyticsSwitchRow = () => {
const legacyAnalytics = useLegacyAnalytics();
const analytics = useAnalytics();
const isAnalyticsEnabled = useSelector(selectIsAnalyticsEnabled);
const handleAnalyticsChange = (isEnabled: boolean) => {
if (isEnabled) {
legacyAnalytics.enable();
analytics.enable();
return;
}
legacyAnalytics.disable();
analytics.disable();
};

View File

@@ -1,3 +1,3 @@
export { useNativeServices, NativeServicesProvider } from './NativeServicesProvider';
export type { NativeServices } from './nativeServices';
export { useAnalytics, useLegacyAnalytics } from './useAnalytics';
export { useAnalytics } from './useAnalytics';

View File

@@ -1,10 +1,9 @@
import type { MMKV } from 'react-native-mmkv';
import { CommonServices } from '@suite-common/redux-utils';
import type { NativeAnalyticsDep, NativeLegacyAnalyticsDep } from '@suite-native/analytics';
import type { NativeAnalyticsDep } from '@suite-native/analytics';
export type NativeServices = CommonServices &
NativeAnalyticsDep &
NativeLegacyAnalyticsDep & {
NativeAnalyticsDep & {
getMMKVStorage: () => Promise<MMKV>;
};

View File

@@ -5,10 +5,3 @@ export const useAnalytics = () => {
return suiteServices.analytics;
};
/** @deprecated use `useAnalytics` instead */
export const useLegacyAnalytics = () => {
const suiteServices = useNativeServices();
return suiteServices.legacyAnalytics;
};

View File

@@ -17,7 +17,7 @@ import { selectIsSuiteSyncEnabled } from '@suite-common/suite-sync';
import { Route } from '@suite-common/suite-types';
import { selectSelectedDevice } from '@suite-common/wallet-core';
import { AddressDisplayOptions } from '@suite-common/wallet-types';
import { createAnalytics, createLegacyAnalytics } from '@suite-native/analytics';
import { createAnalytics } from '@suite-native/analytics';
import { forgetBluetoothDeviceThunk } from '@suite-native/bluetooth';
import { selectTokenDefinitionsEnabledNetworks } from '@suite-native/discovery';
import { reportSecurityCheck } from '@suite-native/sentry';
@@ -74,7 +74,6 @@ export const createNativeCompositionRoot = (deps: NativeAppDeps): NativeServices
suiteSync,
platformEncryption,
getMMKVStorage: () => deps.mmkvStorage.getMMKV(),
legacyAnalytics: createLegacyAnalytics(),
analytics: createAnalytics(),
reportSecurityCheck,
saveAs: (data, fileName) =>

View File

@@ -1,11 +1,7 @@
import type { MMKV } from 'react-native-mmkv';
import { ExtraDependenciesStatic } from '@suite-common/redux-utils';
import {
analyticsMock,
extraDependenciesCommonMock,
legacyAnalyticsMock,
} from '@suite-common/test-utils';
import { analyticsMock, extraDependenciesCommonMock } from '@suite-common/test-utils';
import { NativeServices } from '@suite-native/services';
type ExtraDependenciesNativeMock = ExtraDependenciesStatic & { services: NativeServices };
@@ -15,7 +11,6 @@ export const extraDependenciesNativeMock: ExtraDependenciesNativeMock = {
services: {
...extraDependenciesCommonMock.services,
analytics: analyticsMock, // To satisfy Native specific type as ExtraDependenciesStatic tightness it
legacyAnalytics: legacyAnalyticsMock, // To satisfy Native specific type as ExtraDependenciesStatic tightness it
getMMKVStorage: () => Promise.resolve({} as MMKV),
},
};

View File

@@ -1,6 +1,6 @@
import { useCountryFilteredData } from '@suite-common/trading';
import { Form, useForm } from '@suite-native/forms';
import { useAnalytics, useLegacyAnalytics } from '@suite-native/services';
import { useAnalytics } from '@suite-native/services';
import {
renderHookWithBasicProvider,
renderHookWithStoreProvider,
@@ -27,7 +27,6 @@ jest.mock('@suite-native/services', () => {
return {
...original,
useAnalytics: jest.fn(),
useLegacyAnalytics: jest.fn(),
};
});
@@ -53,9 +52,6 @@ describe('CountryOfResidencePicker', () => {
(useAnalytics as jest.Mock).mockReturnValue({
report: reportMock,
});
(useLegacyAnalytics as jest.Mock).mockReturnValue({
report: reportMock,
});
});
afterEach(() => {

View File

@@ -2,7 +2,7 @@ import { RouteProp } from '@react-navigation/native';
import { EventType } from '@suite-native/analytics';
import { SettingsStackParamList, SettingsStackRoutes } from '@suite-native/navigation';
import { useAnalytics, useLegacyAnalytics } from '@suite-native/services';
import { useAnalytics } from '@suite-native/services';
import { renderWithStoreProvider, screen, userEvent } from '@suite-native/test-utils';
import { SettingsTradingLocationScreen } from '../SettingsTradingLocationScreen';
@@ -16,7 +16,6 @@ jest.mock('@suite-native/services', () => {
return {
...original,
useAnalytics: jest.fn(),
useLegacyAnalytics: jest.fn(),
};
});
@@ -42,9 +41,6 @@ describe('TradingLocationSettingsScreen', () => {
(useAnalytics as jest.Mock).mockReturnValue({
report: reportMock,
});
(useLegacyAnalytics as jest.Mock).mockReturnValue({
report: reportMock,
});
});
afterEach(() => {

View File

@@ -6,7 +6,7 @@ import {
TradingStackParamList,
TradingStackRoutes,
} from '@suite-native/navigation';
import { useAnalytics, useLegacyAnalytics } from '@suite-native/services';
import { useAnalytics } from '@suite-native/services';
import { renderWithStoreProvider, screen, userEvent } from '@suite-native/test-utils';
import {
@@ -29,7 +29,6 @@ jest.mock('@suite-native/services', () => {
return {
...original,
useAnalytics: jest.fn(),
useLegacyAnalytics: jest.fn(),
};
});
@@ -63,9 +62,6 @@ describe('TradingLocationModalScreen', () => {
(useAnalytics as jest.Mock).mockReturnValue({
report: reportMock,
});
(useLegacyAnalytics as jest.Mock).mockReturnValue({
report: reportMock,
});
});
afterEach(() => {

View File

@@ -1,13 +0,0 @@
import { Analytics, QueuedAnalytics } from '@trezor/analytics-uploader';
export type DesktopLegacyAnalyticsDep = {
legacyAnalytics: Analytics<any>;
};
/** @deprecated use `createAnalytics` instead */
export const createLegacyAnalytics = (): Analytics<any> =>
new QueuedAnalytics<any>({
version: process.env.VERSION!,
app: 'suite',
useQueue: true,
});

View File

@@ -1,4 +0,0 @@
import { Analytics } from '@trezor/analytics-uploader';
// @TODO: we have to type dispatch correctly in desktop/native/common
export const getTypedDesktopLegacyAnalytics = (legacyAnalytics: Analytics<any>) => legacyAnalytics;

View File

@@ -1,4 +1,3 @@
export { createLegacyAnalytics, type DesktopLegacyAnalyticsDep } from './createLegacyAnalytics';
export { createAnalytics, type DesktopAnalyticsDep } from './createAnalytics';
export type { OnboardingAnalytics, AppUpdateEvent, FirmwareSource } from './definitions';
export { EventType, AppUpdateEventStatus } from './constants';

View File

@@ -18,7 +18,7 @@ export class AnalyticsFixture {
/**
* Finds analytics event by type. Works for both legacy events and events
* migrated from legacyAnalytics (e.g. DeviceConnect, TransportType when migrated).
* migrated from older analytics tracking (e.g. DeviceConnect, TransportType when migrated).
*/
findAnalyticsEventByType<T extends SuiteDesktopAnalyticsEventsForE2e>(eventType: T['type']) {
const event = this.requests.find(req => req.c_type === eventType) as EventPayload<T>;