From b37bbf9cc6a4f3259db801c02bf9404d06b25396 Mon Sep 17 00:00:00 2001 From: Mounir Hamzaoui Date: Wed, 22 Jan 2025 18:03:17 +0100 Subject: [PATCH] feat: add analytics to add account v2 flow --- .changeset/fifty-worms-remain.md | 5 + .../RootNavigator/types/BaseNavigator.ts | 1 + .../newArch/features/Accounts/Navigator.tsx | 46 +++- .../components/AccountListDrawer/index.tsx | 67 ++--- .../AccountQuickActionsDrawer/index.tsx | 49 ++-- .../useAccountQuickActionDrawerViewModel.ts | 11 +- .../components/AddFundsButton/index.tsx | 52 +++- .../Accounts/screens/AccountsList/index.tsx | 1 + .../Accounts/screens/AddAccount/types.ts | 1 + .../screens/AddAccountSuccess/index.tsx | 6 +- .../screens/AddAccountWarning/index.tsx | 6 +- .../screens/ScanDeviceAccounts/index.tsx | 19 +- .../screens/ScanDeviceAccounts/types.ts | 2 + .../useScanDeviceAccountsViewModel.ts | 49 +++- .../features/AssetSelection/Navigator.tsx | 57 ++++- .../screens/SelectCrypto/index.tsx | 16 +- .../SelectCrypto/useSelectCryptoViewModel.ts | 24 +- .../screens/SelectNetwork/index.tsx | 17 +- .../useSelectNetworkViewModel.ts | 20 +- .../newArch/features/AssetSelection/types.ts | 3 + .../SelectDevice/useSelectDeviceViewModel.ts | 7 +- .../hooks/__tests__/useAnalytics.test.ts | 234 +++++++++++++++++ .../src/newArch/hooks/useAnalytics/enums.ts | 40 +++ .../src/newArch/hooks/useAnalytics/index.ts | 236 ++++++++++++++++++ .../src/newArch/hooks/useAnalytics/types.ts | 9 + .../Modals/InstalledAppModal.tsx | 1 + .../RequestAccount/02-SelectAccount.tsx | 1 + .../src/screens/Swap/Form/Summary/index.tsx | 1 + .../screens/Swap/SubScreens/SelectAccount.tsx | 1 + .../src/screens/WalletCentricAsset/index.tsx | 1 + 30 files changed, 856 insertions(+), 127 deletions(-) create mode 100644 .changeset/fifty-worms-remain.md create mode 100644 apps/ledger-live-mobile/src/newArch/hooks/__tests__/useAnalytics.test.ts create mode 100644 apps/ledger-live-mobile/src/newArch/hooks/useAnalytics/enums.ts create mode 100644 apps/ledger-live-mobile/src/newArch/hooks/useAnalytics/index.ts create mode 100644 apps/ledger-live-mobile/src/newArch/hooks/useAnalytics/types.ts diff --git a/.changeset/fifty-worms-remain.md b/.changeset/fifty-worms-remain.md new file mode 100644 index 000000000000..0a1bba08ff1b --- /dev/null +++ b/.changeset/fifty-worms-remain.md @@ -0,0 +1,5 @@ +--- +"live-mobile": minor +--- + +Add analytics to all add account v2 workflow diff --git a/apps/ledger-live-mobile/src/components/RootNavigator/types/BaseNavigator.ts b/apps/ledger-live-mobile/src/components/RootNavigator/types/BaseNavigator.ts index 96b5ba214639..87f7dd7637e3 100644 --- a/apps/ledger-live-mobile/src/components/RootNavigator/types/BaseNavigator.ts +++ b/apps/ledger-live-mobile/src/components/RootNavigator/types/BaseNavigator.ts @@ -325,6 +325,7 @@ export type BaseNavigatorStackParamList = { context?: "addAccounts" | "receiveFunds"; token?: string; currency?: string; + sourceScreenName?: string; } // in some cases we need to pass directly the context to the navigator and let it handle the logic >; [NavigatorName.Assets]?: Partial>; diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/Navigator.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/Navigator.tsx index 76fbe196d636..facd0c34c2c0 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/Navigator.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/Navigator.tsx @@ -3,7 +3,7 @@ import { Platform } from "react-native"; import { createStackNavigator } from "@react-navigation/stack"; import { useTheme } from "styled-components/native"; import { useNavigation, useRoute } from "@react-navigation/native"; -import { ScreenName } from "~/const"; +import { NavigatorName, ScreenName } from "~/const"; import { getStackNavigatorConfig } from "~/navigation/navigatorConfig"; import { track } from "~/analytics"; import type { NetworkBasedAddAccountNavigator } from "LLM/features/Accounts/screens/AddAccount/types"; @@ -17,25 +17,51 @@ import SelectAccounts from "./screens/SelectAccounts"; import AddAccountsWarning from "./screens/AddAccountWarning"; import NoAssociatedAccountsView from "./screens/NoAssociatedAccountsView"; import CloseWithConfirmation from "LLM/components/CloseWithConfirmation"; -import { StackNavigatorNavigation } from "~/components/RootNavigator/types/helpers"; -import { BaseNavigatorStackParamList } from "~/components/RootNavigator/types/BaseNavigator"; +import { + BaseComposite, + StackNavigatorNavigation, + StackNavigatorProps, +} from "~/components/RootNavigator/types/helpers"; import { NavigationHeaderCloseButtonAdvanced } from "~/components/NavigationHeaderCloseButton"; +import useAnalytics from "LLM/hooks/useAnalytics"; +import { AddAccountsNavigatorParamList } from "~/components/RootNavigator/types/AddAccountsNavigator"; +type NavigationProps = BaseComposite< + StackNavigatorProps +>; export default function Navigator() { const { colors } = useTheme(); - const route = useRoute(); + const route = useRoute(); const accountListUIFF = useFeature("llmAccountListUI"); - const navigation = useNavigation>(); + const navigation = useNavigation>(); + + const { analyticsMetadata } = useAnalytics("addAccounts"); + + const exitProcess = useCallback(() => { + const rootParent = navigation.getParent(); + // this is the only way to go back to the root navigator + navigation.replace(rootParent?.getState().routeNames[0] as keyof AddAccountsNavigatorParamList); + }, [navigation]); const onClose = useCallback(() => { track("button_clicked", { button: "Close", screen: route.name, }); - const rootParent = navigation.getParent(); - // this is the only way to go back to the root navigator - navigation.replace(rootParent?.getState().routeNames[0] as keyof BaseNavigatorStackParamList); - }, [route, navigation]); + exitProcess(); + }, [route, exitProcess]); + + const onExitScanDeviceAccounts = useCallback(() => { + const clickMetadata = analyticsMetadata[ScreenName.ScanDeviceAccounts]?.onClose; + track(clickMetadata?.eventName, clickMetadata?.payload); + exitProcess(); + }, [exitProcess, analyticsMetadata]); + + const onBackScanDeviceAccounts = useCallback(() => { + const clickMetadata = analyticsMetadata[ScreenName.ScanDeviceAccounts]?.onBack; + track(clickMetadata?.eventName, clickMetadata?.payload); + navigation.goBack(); + }, [navigation, analyticsMetadata]); const stackNavigationConfig = useMemo( () => ({ @@ -67,6 +93,8 @@ export default function Navigator() { options={{ headerTitle: "", headerTransparent: true, + headerRight: () => , + headerLeft: () => , }} /> {/* Select Accounts */} diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListDrawer/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListDrawer/index.tsx index 3707a2f1c75c..376bad50dc9b 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListDrawer/index.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountListDrawer/index.tsx @@ -9,24 +9,20 @@ import { useTheme } from "styled-components/native"; import getAccountListKeyExtractor from "../../utils/getAccountListKeyExtractor"; import AccountListEmpty from "../AccountListEmpty"; import { useTranslation } from "react-i18next"; +import { TrackScreen } from "~/analytics"; +import useAnalytics from "LLM/hooks/useAnalytics"; type AccountListDrawerProps = { isOpen: boolean; onClose: () => void; - onBack: () => void; data: (Account | TokenAccount)[]; onPressAccount: (account: Account | TokenAccount) => void; }; -const AccountListDrawer = ({ - isOpen, - onClose, - onBack, - data, - onPressAccount, -}: AccountListDrawerProps) => { +const AccountListDrawer = ({ isOpen, onClose, data, onPressAccount }: AccountListDrawerProps) => { const { colors } = useTheme(); const { t } = useTranslation(); + const { analyticsMetadata } = useAnalytics("addAccounts", "tata"); const renderItem = useCallback( ({ item: account }: ListRenderItemInfo) => { @@ -43,33 +39,38 @@ const AccountListDrawer = ({ const keyExtractor = useCallback(getAccountListKeyExtractor, []); + const pageTrackingEvent = analyticsMetadata.AddFunds?.onAccessScreen; + return ( - ( - + <> + {isOpen && ( + )} - > - - } - style={{ paddingHorizontal: 16 }} - ListEmptyComponent={} - /> - - + ( + + )} + > + + } + style={{ paddingHorizontal: 16 }} + ListEmptyComponent={} + /> + + + ); }; diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/index.tsx index 1f0d5d3705c1..96d6931470f1 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/index.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/index.tsx @@ -8,11 +8,12 @@ import TransferButton from "~/components/TransferButton"; import { CryptoOrTokenCurrency } from "@ledgerhq/types-cryptoassets"; import CustomHeader from "./CustomHeader"; import { useTheme } from "styled-components/native"; +import { TrackScreen } from "~/analytics"; +import useAnalytics from "LLM/hooks/useAnalytics"; type AccountListDrawerProps = { isOpen: boolean; onClose: () => void; - onBack: () => void; account: Account | TokenAccount | null; currency: CryptoOrTokenCurrency; }; @@ -20,7 +21,6 @@ type AccountListDrawerProps = { const AccountQuickActionsDrawer = ({ isOpen, onClose, - onBack, account, currency, }: AccountListDrawerProps) => { @@ -29,29 +29,34 @@ const AccountQuickActionsDrawer = ({ currency, }); const { colors } = useTheme(); + const { analyticsMetadata } = useAnalytics("addAccounts"); + const pageTrackingEvent = analyticsMetadata.AddFunds?.onQuickActionOpen; return ( - ( - + <> + {isOpen && ( + )} - > - - {actions.map((button, index) => ( - - - - ))} - - + ( + + )} + > + + {actions.map((button, index) => ( + + + + ))} + + + ); }; diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/useAccountQuickActionDrawerViewModel.ts b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/useAccountQuickActionDrawerViewModel.ts index c26f2367daac..8aa64d5bff0a 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/useAccountQuickActionDrawerViewModel.ts +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AccountQuickActionsDrawer/useAccountQuickActionDrawerViewModel.ts @@ -9,6 +9,7 @@ import { StackNavigatorNavigation } from "~/components/RootNavigator/types/helpe import { IconType } from "@ledgerhq/native-ui/components/Icon/type"; import { StyleProp, ViewStyle } from "react-native"; import { track } from "~/analytics"; +import { AnalyticButtons, AnalyticEvents } from "~/newArch/hooks/useAnalytics/enums"; type ActionItem = { title: string; @@ -40,9 +41,8 @@ export default function useAccountQuickActionDrawerViewModel({ const actions: ActionItem[] = [ RECEIVE && { eventProperties: { - button: "transfer_receive", - //page, TODO: add it in the analytics ticket - drawer: "trade", + button: AnalyticButtons.Receive, + page: AnalyticEvents.FundingQuickAction, }, title: t("transfer.receive.title"), description: t("transfer.receive.description"), @@ -54,9 +54,8 @@ export default function useAccountQuickActionDrawerViewModel({ }, BUY && { eventProperties: { - button: "transfer_buy", - //page, TODO: add it in the analytics ticket - drawer: "trade", + button: AnalyticButtons.Buy, + page: AnalyticEvents.FundingQuickAction, }, title: t("transfer.buy.title"), description: t("transfer.buy.description"), diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AddFundsButton/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AddFundsButton/index.tsx index a65d6c5c6bad..38abb651fae2 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AddFundsButton/index.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/components/AddFundsButton/index.tsx @@ -8,13 +8,17 @@ import { Account, AccountLike } from "@ledgerhq/types-live"; import { CryptoOrTokenCurrency } from "@ledgerhq/types-cryptoassets"; import AccountListDrawer from "../AccountListDrawer"; import AccountQuickActionsDrawer from "../AccountQuickActionsDrawer"; +import useAnalytics from "LLM/hooks/useAnalytics"; +import { getDefaultAccountName } from "@ledgerhq/live-wallet/accountName"; export default function AddFundsButton({ accounts, currency, + sourceScreenName, }: { accounts: Account[]; currency: CryptoOrTokenCurrency; + sourceScreenName: string; }) { const { t } = useTranslation(); const [isAccountListDrawerOpen, setIsAccountListDrawerOpen] = useState(false); @@ -23,23 +27,49 @@ export default function AddFundsButton({ const [selectedAccount, setSelectedAccount] = useState( accounts.length === 1 ? accounts[0] : null, ); - const openFundOrAccountListDrawer = () => { - track("button_clicked", { button: "Add a new account" }); + const { analyticsMetadata } = useAnalytics("addAccounts", sourceScreenName); + + const openFundOrAccountListDrawer = useCallback(() => { + let clickMetadata; if (accounts.length === 1) { + clickMetadata = analyticsMetadata.AddFunds?.onQuickActionOpen; setIsAccountQuickActionsDrawerOpen(true); - } else setIsAccountListDrawerOpen(true); - }; + } else { + clickMetadata = analyticsMetadata.AddFunds?.onOpenDrawer; + setIsAccountListDrawerOpen(true); + } + track(clickMetadata.eventName, clickMetadata.payload); + }, [ + accounts.length, + analyticsMetadata.AddFunds?.onQuickActionOpen, + analyticsMetadata.AddFunds?.onOpenDrawer, + ]); - const closeAccountListDrawer = () => setIsAccountListDrawerOpen(false); + const closeAccountListDrawer = useCallback(() => { + const clickMetadata = analyticsMetadata.AddFunds?.onCloseDrawer; + setIsAccountListDrawerOpen(false); + track(clickMetadata.eventName, clickMetadata.payload); + }, [analyticsMetadata.AddFunds?.onCloseDrawer]); - const handleOnSelectAccoount = useCallback((account: AccountLike) => { - closeAccountListDrawer(); - setSelectedAccount(account); - setIsAccountQuickActionsDrawerOpen(true); - }, []); + const handleOnSelectAccoount = useCallback( + (account: AccountLike) => { + closeAccountListDrawer(); + setSelectedAccount(account); + setIsAccountQuickActionsDrawerOpen(true); + const selectAccountMetadata = analyticsMetadata.AddFunds?.onSelectAccount; + track(selectAccountMetadata.eventName, { + ...selectAccountMetadata.payload, + account: getDefaultAccountName(account), + currency: currency.id, + }); + }, + [closeAccountListDrawer, currency, analyticsMetadata.AddFunds?.onSelectAccount], + ); const handleOnCloseQuickActions = () => { + const clickMetadata = analyticsMetadata.AddFunds?.onQuickActionClose; setIsAccountQuickActionsDrawerOpen(false); + track(clickMetadata.eventName, clickMetadata.payload); }; return ( @@ -55,14 +85,12 @@ export default function AddFundsButton({ diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AccountsList/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AccountsList/index.tsx index c9b441b710b9..3e2b9a8bb291 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AccountsList/index.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AccountsList/index.tsx @@ -86,6 +86,7 @@ export default function AccountsList({ route }: Props) { params: { currency: currency.id, context: "addAccounts", + sourceScreenName: ScreenName.AccountsList, }, }); } else { diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/types.ts b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/types.ts index 618d1e74b684..41df561d20b8 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/types.ts +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/types.ts @@ -9,6 +9,7 @@ type CommonParams = { onSuccess?: () => void; onCloseNavigation?: () => void; currency: CryptoOrTokenCurrency; + sourceScreenName?: string; }; export type NetworkBasedAddAccountNavigator = { [ScreenName.ScanDeviceAccounts]: CommonParams & { diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccountSuccess/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccountSuccess/index.tsx index 9f70664e94af..5851ab5a1bbd 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccountSuccess/index.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccountSuccess/index.tsx @@ -98,7 +98,11 @@ export default function AddAccountsSuccess({ route }: Props) { /> - + - + (); + const { context, sourceScreenName } = route.params || {}; + + const { analyticsMetadata } = useAnalytics(context, sourceScreenName); const { alreadyEmptyAccount, @@ -63,6 +70,7 @@ function ScanDeviceAccounts() { } = useScanDeviceAccountsViewModel({ existingAccounts, blacklistedTokenIds, + analyticsMetadata, }); const emptyTexts = { @@ -71,6 +79,11 @@ function ScanDeviceAccounts() { ), }; + const pageTrackingEvent = + sections?.length === 0 + ? analyticsMetadata.ScanDeviceAccounts?.onAccessScreen + : analyticsMetadata.AccountsFound?.onAccessScreen; + return ( - + - {scanning || !scannedAccounts.length ? ( ) )} - {scanning ? : null} {sections.map(({ id, selectable, defaultSelected, data }, i) => { diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/types.ts b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/types.ts index da72a6c2204e..d6cf0b719e97 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/types.ts +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/types.ts @@ -3,6 +3,7 @@ import { BaseComposite, StackNavigatorProps } from "~/components/RootNavigator/t import { NetworkBasedAddAccountNavigator } from "../AddAccount/types"; import { ScreenName } from "~/const"; import { AddAccountSupportLink } from "@ledgerhq/live-wallet/addAccounts"; +import { AnalyticMetadata } from "LLM/hooks/useAnalytics/types"; export type SubAccountEnhanced = SubAccount & { parentAccount: Account; @@ -17,6 +18,7 @@ export type ScanDeviceAccountsNavigationProps = BaseComposite< export type ScanDeviceAccountsViewModelProps = { existingAccounts: Account[]; blacklistedTokenIds?: string[]; + analyticsMetadata?: AnalyticMetadata; }; export type ScanDeviceAccountsFooterProps = { diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/useScanDeviceAccountsViewModel.ts b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/useScanDeviceAccountsViewModel.ts index 6750884f901d..ba523b461cf8 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/useScanDeviceAccountsViewModel.ts +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/ScanDeviceAccounts/useScanDeviceAccountsViewModel.ts @@ -19,10 +19,12 @@ import { useMaybeAccountName } from "~/reducers/wallet"; import { setAccountName } from "@ledgerhq/live-wallet/store"; import { addAccountsAction } from "@ledgerhq/live-wallet/addAccounts"; import type { ScanDeviceAccountsNavigationProps, ScanDeviceAccountsViewModelProps } from "./types"; +import { track } from "~/analytics"; export default function useScanDeviceAccountsViewModel({ existingAccounts, blacklistedTokenIds, + analyticsMetadata, }: ScanDeviceAccountsViewModelProps) { const [scanning, setScanning] = useState(true); const navigation = useNavigation(); @@ -96,16 +98,24 @@ export default function useScanDeviceAccountsViewModel({ setCancelled(false); startSubscription(); }, [startSubscription]); - const stopSubscription = useCallback((syncUI = true) => { - if (scanSubscription.current) { - scanSubscription.current.unsubscribe(); - scanSubscription.current = null; + const stopSubscription = useCallback( + (syncUI = true) => { + if (scanSubscription.current) { + scanSubscription.current.unsubscribe(); + scanSubscription.current = null; - if (syncUI) { - setScanning(false); + if (syncUI) { + setScanning(false); + const stopScanMetadata = analyticsMetadata?.ScanDeviceAccounts.onStopScan; + if (stopScanMetadata) + track(stopScanMetadata.eventName, { + ...stopScanMetadata.payload, + }); + } } - } - }, []); + }, + [analyticsMetadata?.ScanDeviceAccounts.onStopScan], + ); const quitFlow = useCallback(() => { navigation.navigate(NavigatorName.Accounts); }, [navigation]); @@ -122,8 +132,13 @@ export default function useScanDeviceAccountsViewModel({ const selectAll = useCallback( (accounts: Account[]) => { setSelectedIds(uniq([...selectedIds, ...accounts.map(a => a.id)])); + const selectAllMetadata = analyticsMetadata?.AccountsFound.onSelectAll; + if (selectAllMetadata) + track(selectAllMetadata.eventName, { + ...selectAllMetadata.payload, + }); }, - [selectedIds], + [selectedIds, analyticsMetadata?.AccountsFound.onSelectAll], ); const unselectAll = useCallback( (accounts: Account[]) => { @@ -160,6 +175,20 @@ export default function useScanDeviceAccountsViewModel({ accountsToAdd: accountsToAdd, }); } + + const continueMetadata = analyticsMetadata?.AccountsFound.onContinue; + if (continueMetadata) + track(continueMetadata.eventName, { + ...continueMetadata.payload, + }); + + const successMetadata = analyticsMetadata?.AccountsFound.onAccountsAdded; + if (successMetadata) + track(successMetadata.eventName, { + ...successMetadata.payload, + currency, + amount: accountsToAdd.length, + }); }, [ currency, inline, @@ -169,6 +198,8 @@ export default function useScanDeviceAccountsViewModel({ scannedAccounts, selectedIds, dispatch, + analyticsMetadata?.AccountsFound.onContinue, + analyticsMetadata?.AccountsFound.onAccountsAdded, ]); const onCancel = useCallback(() => { diff --git a/apps/ledger-live-mobile/src/newArch/features/AssetSelection/Navigator.tsx b/apps/ledger-live-mobile/src/newArch/features/AssetSelection/Navigator.tsx index 7f85ce9fed2c..66b05c193bab 100644 --- a/apps/ledger-live-mobile/src/newArch/features/AssetSelection/Navigator.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/AssetSelection/Navigator.tsx @@ -17,6 +17,7 @@ import { NavigationHeaderBackButton } from "~/components/NavigationHeaderBackBut import { AssetSelectionNavigatorParamsList } from "./types"; import { BaseComposite, StackNavigatorProps } from "~/components/RootNavigator/types/helpers"; import CloseWithConfirmation from "LLM/components/CloseWithConfirmation"; +import useAnalytics from "../../hooks/useAnalytics"; type NavigationProps = BaseComposite< StackNavigatorProps @@ -27,23 +28,39 @@ export default function Navigator() { const route = useRoute(); const hasClosedNetworkBanner = useSelector(hasClosedNetworkBannerSelector); - const { token, currency } = route.params || {}; + const { token, currency, context, sourceScreenName } = route.params || {}; const navigation = useNavigation(); + const { analyticsMetadata } = useAnalytics(context, sourceScreenName); - const handleOnCloseAssetSelectionNavigator = useCallback(() => { - track("button_clicked", { - button: "Close", - screen: route.name, - }); - navigation.getParent()?.goBack(); - }, [route, navigation]); + const handleOnCloseAssetSelectionNavigator = useCallback( + (screenName: string) => () => { + const closeMetadata = analyticsMetadata[screenName]?.onClose; + if (closeMetadata) + track(closeMetadata.eventName, { + ...closeMetadata.payload, + }); + navigation.getParent()?.goBack(); + }, + [navigation, analyticsMetadata], + ); + + const handleOnBack = useCallback( + (screenName: string) => (nav: typeof navigation) => { + const backMetadata = analyticsMetadata[screenName]?.onBack; + if (backMetadata) + track(backMetadata.eventName, { + ...backMetadata.payload, + }); + nav.goBack(); + }, + [analyticsMetadata], + ); const stackNavigationConfig = useMemo( () => ({ ...getStackNavigatorConfig(colors, true), - headerRight: () => , }), - [colors, handleOnCloseAssetSelectionNavigator], + [colors], ); return ( @@ -60,8 +77,18 @@ export default function Navigator() { name={ScreenName.AddAccountsSelectCrypto} component={SelectCrypto} options={{ - headerLeft: () => , title: "", + headerRight: () => ( + + ), + + headerLeft: () => ( + + ), }} initialParams={route.params} /> @@ -70,14 +97,18 @@ export default function Navigator() { name={ScreenName.SelectNetwork} component={SelectNetwork} options={{ - headerLeft: () => , headerTitle: "", + headerLeft: () => ( + + ), headerRight: () => ( {hasClosedNetworkBanner && ( )} - {stackNavigationConfig.headerRight()} + ), }} diff --git a/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectCrypto/index.tsx b/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectCrypto/index.tsx index dae63aa1ce15..e03adff5454b 100644 --- a/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectCrypto/index.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectCrypto/index.tsx @@ -18,6 +18,7 @@ import BigCurrencyRow from "~/components/BigCurrencyRow"; import { StackNavigatorProps } from "~/components/RootNavigator/types/helpers"; import { AssetSelectionNavigatorParamsList } from "../../types"; import useSelectCryptoViewModel from "./useSelectCryptoViewModel"; +import useAnalytics from "../../../../hooks/useAnalytics"; const SEARCH_KEYS = getEnv("CRYPTO_ASSET_SEARCH_KEYS"); @@ -34,7 +35,14 @@ const renderEmptyList = () => ( export default function SelectCrypto({ route, }: StackNavigatorProps) { - const { filterCurrencyIds, currency: paramsCurrency, context } = route?.params || {}; + const { + filterCurrencyIds, + currency: paramsCurrency, + context, + sourceScreenName, + } = route?.params || {}; + + const { analyticsMetadata } = useAnalytics(context, sourceScreenName); const { titleText, titleTestId, @@ -42,7 +50,7 @@ export default function SelectCrypto({ onPressItem, debounceTrackOnSearchChange, providersLoadingStatus, - } = useSelectCryptoViewModel({ context, filterCurrencyIds, paramsCurrency }); + } = useSelectCryptoViewModel({ context, filterCurrencyIds, paramsCurrency, analyticsMetadata }); const renderList = useCallback( (items: CryptoOrTokenCurrency[]) => ( @@ -87,9 +95,11 @@ export default function SelectCrypto({ } }, [providersLoadingStatus, list, renderList, debounceTrackOnSearchChange]); + const pageTrackingEvent = analyticsMetadata?.AddAccountsSelectCrypto?.onAccessScreen; + return ( - + {titleText} diff --git a/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectCrypto/useSelectCryptoViewModel.ts b/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectCrypto/useSelectCryptoViewModel.ts index ba3ec82a2ebc..8eafd6b33b82 100644 --- a/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectCrypto/useSelectCryptoViewModel.ts +++ b/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectCrypto/useSelectCryptoViewModel.ts @@ -15,16 +15,20 @@ import type { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets import { AssetSelectionNavigationProps, CommonParams } from "../../types"; import { useGroupedCurrenciesByProvider } from "@ledgerhq/live-common/deposit/index"; import { LoadingBasedGroupedCurrencies } from "@ledgerhq/live-common/deposit/type"; +import { AnalyticMetadata } from "../../../../hooks/useAnalytics/types"; type SelectCryptoViewModelProps = Pick & { filterCurrencyIds?: string[]; paramsCurrency?: string; + sourceScreenName?: string; + analyticsMetadata: AnalyticMetadata; }; export default function useSelectCryptoViewModel({ context, filterCurrencyIds, paramsCurrency, + analyticsMetadata, }: SelectCryptoViewModelProps) { const { t } = useTranslation(); const filterCurrencyIdsSet = useMemo( @@ -42,10 +46,12 @@ export default function useSelectCryptoViewModel({ const onPressItem = useCallback( (curr: CryptoCurrency | TokenCurrency) => { - track("asset_clicked", { - asset: curr.name, - page: "Choose a crypto to secure", - }); + const clickMetadata = analyticsMetadata.AddAccountsSelectCrypto?.onAssetClick; + if (clickMetadata) + track(clickMetadata?.eventName, { + asset: curr.name, + ...clickMetadata.payload, + }); const provider = currenciesByProvider.find(elem => elem.currenciesByNetwork.some( @@ -59,6 +65,7 @@ export default function useSelectCryptoViewModel({ context, currency: curr.id, ...(context === "receiveFunds" && { filterCurrencyIds }), + sourceScreenName: ScreenName.AddAccountsSelectCrypto, }); return; } @@ -88,7 +95,14 @@ export default function useSelectCryptoViewModel({ }); } }, - [currenciesByProvider, accounts, navigation, filterCurrencyIds, context], + [ + currenciesByProvider, + accounts, + navigation, + filterCurrencyIds, + context, + analyticsMetadata.AddAccountsSelectCrypto?.onAssetClick, + ], ); useEffect(() => { diff --git a/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectNetwork/index.tsx b/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectNetwork/index.tsx index 339876e0e1ee..42861eddb5fe 100644 --- a/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectNetwork/index.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectNetwork/index.tsx @@ -12,6 +12,7 @@ import { AssetSelectionNavigatorParamsList } from "../../types"; import useSelectNetworkViewModel from "./useSelectNetworkViewModel"; import NetworkBanner from "../../components/NetworkBanner"; import { LoadingStatus } from "@ledgerhq/live-common/deposit/type"; +import useAnalytics from "../../../../hooks/useAnalytics"; const keyExtractor = (elem: CryptoWithAccounts) => elem.crypto.id; @@ -20,8 +21,10 @@ const AnimatedView = Animatable.View; export default function SelectNetwork({ route, }: StackNavigatorProps) { - const { filterCurrencyIds, context, currency, inline } = route.params; + const { filterCurrencyIds, context, currency, inline, sourceScreenName } = route.params; const { t } = useTranslation(); + const { analyticsMetadata } = useAnalytics(context, sourceScreenName); + const { hideBanner, clickLearn, @@ -34,7 +37,13 @@ export default function SelectNetwork({ subTitleTestId, listTestId, providersLoadingStatus, - } = useSelectNetworkViewModel({ filterCurrencyIds, context, currency, inline }); + } = useSelectNetworkViewModel({ + filterCurrencyIds, + context, + currency, + inline, + analyticsMetadata, + }); const renderItem = useCallback( ({ item }: { item: CryptoWithAccounts }) => ( @@ -51,9 +60,11 @@ export default function SelectNetwork({ [onPressItem, t], ); + const pageTrackingEvent = analyticsMetadata.SelectNetwork?.onAccessScreen; + return ( <> - + {titleText} diff --git a/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectNetwork/useSelectNetworkViewModel.ts b/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectNetwork/useSelectNetworkViewModel.ts index a73ceb7564b8..91ba7856f9f0 100644 --- a/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectNetwork/useSelectNetworkViewModel.ts +++ b/apps/ledger-live-mobile/src/newArch/features/AssetSelection/screens/SelectNetwork/useSelectNetworkViewModel.ts @@ -23,6 +23,7 @@ export default function useSelectNetworkViewModel({ context, currency, inline, + analyticsMetadata, }: SelectNetworkRouteParams) { const navigation = useNavigation(); @@ -112,10 +113,12 @@ export default function useSelectNetworkViewModel({ const processNetworkSelection = useCallback( (selectedCurrency: CryptoCurrency | TokenCurrency) => { - track("network_clicked", { - network: selectedCurrency.name, - page: "Choose a network", - }); + const clickMetadata = analyticsMetadata?.SelectNetwork?.onNetworkClick; + if (clickMetadata) + track(clickMetadata.eventName, { + network: selectedCurrency.name, + ...clickMetadata.payload, + }); const cryptoToSend = provider?.currenciesByNetwork.find(curByNetwork => curByNetwork.type === "TokenCurrency" @@ -165,7 +168,14 @@ export default function useSelectNetworkViewModel({ navigateToDevice(cryptoToSend, false); } }, - [accounts, navigation, provider, context, navigateToDevice], + [ + accounts, + navigation, + provider, + context, + navigateToDevice, + analyticsMetadata?.SelectNetwork?.onNetworkClick, + ], ); const hideBanner = useCallback(() => { diff --git a/apps/ledger-live-mobile/src/newArch/features/AssetSelection/types.ts b/apps/ledger-live-mobile/src/newArch/features/AssetSelection/types.ts index 9c80d14f50ec..4c223a4ca7d5 100644 --- a/apps/ledger-live-mobile/src/newArch/features/AssetSelection/types.ts +++ b/apps/ledger-live-mobile/src/newArch/features/AssetSelection/types.ts @@ -3,15 +3,18 @@ import { NavigatorName, ScreenName } from "~/const"; import { DeviceSelectionNavigatorParamsList } from "../DeviceSelection/types"; import { NetworkBasedAddAccountNavigator } from "../Accounts/screens/AddAccount/types"; import { StackNavigatorProps } from "~/components/RootNavigator/types/helpers"; +import { AnalyticMetadata } from "../../hooks/useAnalytics/types"; export type CommonParams = { context?: "addAccounts" | "receiveFunds"; onSuccess?: () => void; currency?: string; inline?: boolean; + sourceScreenName?: string; }; export type SelectNetworkRouteParams = CommonParams & { filterCurrencyIds?: string[]; + analyticsMetadata?: AnalyticMetadata; }; export type AssetSelectionNavigatorParamsList = { [ScreenName.AddAccountsSelectCrypto]: CommonParams & { diff --git a/apps/ledger-live-mobile/src/newArch/features/DeviceSelection/screens/SelectDevice/useSelectDeviceViewModel.ts b/apps/ledger-live-mobile/src/newArch/features/DeviceSelection/screens/SelectDevice/useSelectDeviceViewModel.ts index 6344fd7dd11b..2877a744b156 100644 --- a/apps/ledger-live-mobile/src/newArch/features/DeviceSelection/screens/SelectDevice/useSelectDeviceViewModel.ts +++ b/apps/ledger-live-mobile/src/newArch/features/DeviceSelection/screens/SelectDevice/useSelectDeviceViewModel.ts @@ -30,7 +30,12 @@ export default function useSelectDeviceViewModel( setDevice(null); const { inline } = route.params; - const params = { ...route.params, ...meta, context }; + const params = { + ...route.params, + ...meta, + context, + sourceScreenName: ScreenName.SelectDevice, + }; if (inline) { navigation.replace(NavigatorName.AddAccounts, { screen: ScreenName.ScanDeviceAccounts, diff --git a/apps/ledger-live-mobile/src/newArch/hooks/__tests__/useAnalytics.test.ts b/apps/ledger-live-mobile/src/newArch/hooks/__tests__/useAnalytics.test.ts new file mode 100644 index 000000000000..933f47afda23 --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/hooks/__tests__/useAnalytics.test.ts @@ -0,0 +1,234 @@ +import { renderHook } from "@tests/test-renderer"; +import useAnalytics from "../useAnalytics"; +import { + AnalyticContexts, + AnalyticEvents, + AnalyticButtons, + AnalyticFlows, + AnalyticPages, +} from "../useAnalytics/enums"; +import { ScreenName } from "~/const"; + +const StaticAnalyticMetadata = { + [ScreenName.AddAccountsSelectCrypto]: { + onAccessScreen: { + eventName: AnalyticEvents.SelectAsset, + payload: { + source: "fakeSourceScreenName", + flow: AnalyticFlows.AddAccount, + }, + }, + onAssetClick: { + eventName: AnalyticEvents.AssetClicked, + payload: { + page: AnalyticPages.AddAccountSelectAsset, + flow: AnalyticFlows.AddAccount, + }, + }, + onAssetSearch: { + eventName: AnalyticEvents.AssetSearched, + payload: { + page: AnalyticPages.AddAccountSelectAsset, + flow: AnalyticFlows.AddAccount, + }, + }, + onBack: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Back, + page: AnalyticPages.AddAccountSelectAsset, + flow: AnalyticFlows.AddAccount, + }, + }, + onClose: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticPages.AddAccountSelectAsset, + flow: AnalyticFlows.AddAccount, + }, + }, + }, + [ScreenName.SelectNetwork]: { + onAccessScreen: { + eventName: AnalyticEvents.SelectNetwork, + payload: { + source: "fakeSourceScreenName", + flow: AnalyticFlows.AddAccount, + }, + }, + onNetworkClick: { + eventName: AnalyticEvents.NetworkClicked, + payload: { + page: AnalyticPages.AddAccountSelectNetwork, + flow: AnalyticFlows.AddAccount, + }, + }, + onBack: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Back, + page: AnalyticPages.AddAccountSelectNetwork, + flow: AnalyticFlows.AddAccount, + }, + }, + onClose: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticPages.AddAccountSelectNetwork, + flow: AnalyticFlows.AddAccount, + }, + }, + }, + [ScreenName.ScanDeviceAccounts]: { + onAccessScreen: { + eventName: AnalyticEvents.AccountLookup, + payload: { + source: "fakeSourceScreenName", + flow: AnalyticFlows.AddAccount, + }, + }, + onStopScan: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.StopScanning, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + onBack: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Back, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + onClose: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + }, + AccountsFound: { + onAccessScreen: { + eventName: AnalyticPages.AccountsFound, + payload: { + source: "fakeSourceScreenName", + flow: AnalyticFlows.AddAccount, + }, + }, + onBack: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Back, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + onClose: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + onSelectAll: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.SelectAll, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + onContinue: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Continue, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + onAccountsAdded: { + eventName: AnalyticEvents.AccountAdded, + payload: { + flow: AnalyticFlows.AddAccount, + }, + }, + }, + AddFunds: { + onOpenDrawer: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.FundMyAccount, + page: AnalyticPages.AddAccountSuccess, + flow: AnalyticFlows.AddAccount, + }, + }, + onAccessScreen: { + eventName: AnalyticPages.FundAccountDrawerList, + payload: { + source: "fakeSourceScreenName", + flow: AnalyticFlows.AddAccount, + }, + }, + onSelectAccount: { + eventName: AnalyticEvents.AccountClicked, + payload: { + page: AnalyticPages.FundAccountDrawerList, + flow: AnalyticFlows.AddAccount, + }, + }, + onCloseDrawer: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticPages.FundAccountDrawerList, + flow: AnalyticFlows.AddAccount, + }, + }, + onQuickActionOpen: { + eventName: AnalyticEvents.FundingQuickAction, + payload: { + source: "fakeSourceScreenName", + flow: AnalyticFlows.AddAccount, + }, + }, + onQuickActionClose: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticEvents.FundingQuickAction, + }, + }, + }, + [ScreenName.AddAccountsSuccess]: { + onClose: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticPages.AddAccountSuccess, + flow: AnalyticFlows.AddAccount, + }, + }, + }, +}; + +describe("useAnalytics", () => { + it("should initialize with default values", () => { + const { result } = renderHook(() => useAnalytics(AnalyticContexts.ReceiveFunds)); + expect(result.current.analyticsMetadata).toEqual({}); + }); + + it("should initialize with default values", () => { + const { result } = renderHook(() => + useAnalytics(AnalyticContexts.AddAccounts, "fakeSourceScreenName"), + ); + expect(result.current.analyticsMetadata).toEqual(StaticAnalyticMetadata); + }); +}); diff --git a/apps/ledger-live-mobile/src/newArch/hooks/useAnalytics/enums.ts b/apps/ledger-live-mobile/src/newArch/hooks/useAnalytics/enums.ts new file mode 100644 index 000000000000..d101e5642891 --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/hooks/useAnalytics/enums.ts @@ -0,0 +1,40 @@ +export enum AnalyticContexts { + AddAccounts = "addAccounts", + ReceiveFunds = "receiveFunds", +} + +export enum AnalyticPages { + AddAccountSelectAsset = "Add Account Select Asset", + AddAccountSelectNetwork = "Add Account Select Network", + AddAccountSuccess = "Add Account Success", + AccountsFound = "Found Accounts", + FundAccountDrawerList = "Fund Account Drawer List", +} + +export enum AnalyticEvents { + AssetClicked = "asset_clicked", + ButtonClicked = "button_clicked", + AssetSearched = "asset_searched", + NetworkClicked = "network_clicked", + AccountAdded = "account_added", + AccountClicked = "account_clicked", + SelectAsset = "Add Account Select Asset", + SelectNetwork = "Add Account Select Network", + AccountLookup = "Looking for accounts", + FundingQuickAction = "Add Account Funding Actions", +} + +export enum AnalyticFlows { + AddAccount = "Add Account", +} + +export enum AnalyticButtons { + SelectAll = "Select all", + Continue = "Continue", + Close = "Close", + FundMyAccount = "Fund my account", + Back = "Back", + StopScanning = "Stop scanning", + Buy = "Buy", + Receive = "Receive", +} diff --git a/apps/ledger-live-mobile/src/newArch/hooks/useAnalytics/index.ts b/apps/ledger-live-mobile/src/newArch/hooks/useAnalytics/index.ts new file mode 100644 index 000000000000..495d918ea891 --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/hooks/useAnalytics/index.ts @@ -0,0 +1,236 @@ +import { useMemo } from "react"; +import { ScreenName } from "~/const"; +import { AnalyticMetadata } from "./types"; +import { + AnalyticButtons, + AnalyticContexts, + AnalyticEvents, + AnalyticFlows, + AnalyticPages, +} from "./enums"; + +export default function useAnalytics( + context: "addAccounts" | "receiveFunds" | undefined, + sourceScreenName?: string, +) { + const analyticsMetadata = useMemo((): AnalyticMetadata => { + switch (context) { + case AnalyticContexts.AddAccounts: + return { + [ScreenName.AddAccountsSelectCrypto]: { + onAccessScreen: { + eventName: AnalyticEvents.SelectAsset, + payload: { + source: sourceScreenName, + flow: AnalyticFlows.AddAccount, + }, + }, + onAssetClick: { + eventName: AnalyticEvents.AssetClicked, + payload: { + page: AnalyticPages.AddAccountSelectAsset, + flow: AnalyticFlows.AddAccount, + }, + }, + onAssetSearch: { + eventName: AnalyticEvents.AssetSearched, + payload: { + page: AnalyticPages.AddAccountSelectAsset, + flow: AnalyticFlows.AddAccount, + }, + }, + onBack: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Back, + page: AnalyticPages.AddAccountSelectAsset, + flow: AnalyticFlows.AddAccount, + }, + }, + onClose: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticPages.AddAccountSelectAsset, + flow: AnalyticFlows.AddAccount, + }, + }, + }, + [ScreenName.SelectNetwork]: { + onAccessScreen: { + eventName: AnalyticEvents.SelectNetwork, + payload: { + source: sourceScreenName, + flow: AnalyticFlows.AddAccount, + }, + }, + onNetworkClick: { + eventName: AnalyticEvents.NetworkClicked, + payload: { + page: AnalyticPages.AddAccountSelectNetwork, + flow: AnalyticFlows.AddAccount, + }, + }, + onBack: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Back, + page: AnalyticPages.AddAccountSelectNetwork, + flow: AnalyticFlows.AddAccount, + }, + }, + onClose: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticPages.AddAccountSelectNetwork, + flow: AnalyticFlows.AddAccount, + }, + }, + }, + [ScreenName.ScanDeviceAccounts]: { + onAccessScreen: { + eventName: AnalyticEvents.AccountLookup, + payload: { + source: sourceScreenName, + flow: AnalyticFlows.AddAccount, + }, + }, + onStopScan: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.StopScanning, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + onBack: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Back, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + onClose: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + }, + AccountsFound: { + onAccessScreen: { + eventName: AnalyticPages.AccountsFound, + payload: { + source: sourceScreenName, + flow: AnalyticFlows.AddAccount, + }, + }, + onBack: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Back, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + onClose: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + onSelectAll: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.SelectAll, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + onContinue: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Continue, + page: AnalyticPages.AccountsFound, + flow: AnalyticFlows.AddAccount, + }, + }, + onAccountsAdded: { + eventName: AnalyticEvents.AccountAdded, + payload: { + flow: AnalyticFlows.AddAccount, + }, + }, + }, + AddFunds: { + onOpenDrawer: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.FundMyAccount, + page: AnalyticPages.AddAccountSuccess, + flow: AnalyticFlows.AddAccount, + }, + }, + onAccessScreen: { + eventName: AnalyticPages.FundAccountDrawerList, + payload: { + source: sourceScreenName, + flow: AnalyticFlows.AddAccount, + }, + }, + onSelectAccount: { + eventName: AnalyticEvents.AccountClicked, + payload: { + page: AnalyticPages.FundAccountDrawerList, + flow: AnalyticFlows.AddAccount, + }, + }, + onCloseDrawer: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticPages.FundAccountDrawerList, + flow: AnalyticFlows.AddAccount, + }, + }, + onQuickActionOpen: { + eventName: AnalyticEvents.FundingQuickAction, + payload: { + source: sourceScreenName, + flow: AnalyticFlows.AddAccount, + }, + }, + onQuickActionClose: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticEvents.FundingQuickAction, + }, + }, + }, + [ScreenName.AddAccountsSuccess]: { + onClose: { + eventName: AnalyticEvents.ButtonClicked, + payload: { + button: AnalyticButtons.Close, + page: AnalyticPages.AddAccountSuccess, + flow: AnalyticFlows.AddAccount, + }, + }, + }, + }; + default: + return {}; + } + }, [context, sourceScreenName]); + + return { + analyticsMetadata, + }; +} diff --git a/apps/ledger-live-mobile/src/newArch/hooks/useAnalytics/types.ts b/apps/ledger-live-mobile/src/newArch/hooks/useAnalytics/types.ts new file mode 100644 index 000000000000..b6af50932339 --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/hooks/useAnalytics/types.ts @@ -0,0 +1,9 @@ +export type AnalyticProps = { + eventName: string; + payload: Record; +}; +export type AnalyticMetadata = { + [key: string]: { + [key: string]: AnalyticProps; + }; +}; diff --git a/apps/ledger-live-mobile/src/screens/MyLedgerDevice/Modals/InstalledAppModal.tsx b/apps/ledger-live-mobile/src/screens/MyLedgerDevice/Modals/InstalledAppModal.tsx index 617280b6dbd1..32283abc66d3 100644 --- a/apps/ledger-live-mobile/src/screens/MyLedgerDevice/Modals/InstalledAppModal.tsx +++ b/apps/ledger-live-mobile/src/screens/MyLedgerDevice/Modals/InstalledAppModal.tsx @@ -55,6 +55,7 @@ const InstallSuccessBar = ({ state, navigation, disable }: Props) => { if (llmNetworkBasedAddAccountFlow?.enabled) navigation.navigate(NavigatorName.AssetSelection, { context: "addAccounts", + sourceScreenName: "InstalleAppModal", }); else navigation.navigate(NavigatorName.AddAccounts); diff --git a/apps/ledger-live-mobile/src/screens/RequestAccount/02-SelectAccount.tsx b/apps/ledger-live-mobile/src/screens/RequestAccount/02-SelectAccount.tsx index cf6ea4f3ba22..396972cc58da 100644 --- a/apps/ledger-live-mobile/src/screens/RequestAccount/02-SelectAccount.tsx +++ b/apps/ledger-live-mobile/src/screens/RequestAccount/02-SelectAccount.tsx @@ -144,6 +144,7 @@ function SelectAccount({ navigation, route }: Props) { currency: currency.id, inline: true, context: "addAccounts", + sourceScreenName: ScreenName.RequestAccountsSelectAccount, onSuccess: () => navigation.navigate(ScreenName.RequestAccountsSelectAccount, route.params), }, diff --git a/apps/ledger-live-mobile/src/screens/Swap/Form/Summary/index.tsx b/apps/ledger-live-mobile/src/screens/Swap/Form/Summary/index.tsx index 26becca41654..9c631c7bbe5b 100644 --- a/apps/ledger-live-mobile/src/screens/Swap/Form/Summary/index.tsx +++ b/apps/ledger-live-mobile/src/screens/Swap/Form/Summary/index.tsx @@ -91,6 +91,7 @@ export function Summary({ provider, swapTx: { swap, status, transaction } }: Pro token: to.currency.id, currency: to.currency.parentCurrency.id, context: "addAccounts", + sourceScreenName: ScreenName.SwapForm, }); } else { navigation.navigate(NavigatorName.AddAccounts, { diff --git a/apps/ledger-live-mobile/src/screens/Swap/SubScreens/SelectAccount.tsx b/apps/ledger-live-mobile/src/screens/Swap/SubScreens/SelectAccount.tsx index 03af2952c841..ee19a1c7e7f5 100644 --- a/apps/ledger-live-mobile/src/screens/Swap/SubScreens/SelectAccount.tsx +++ b/apps/ledger-live-mobile/src/screens/Swap/SubScreens/SelectAccount.tsx @@ -158,6 +158,7 @@ export function SelectAccount({ navigation, route: { params } }: SelectAccountPa }, analyticsPropertyFlow: "swap", context: "addAccounts", + sourceScreenName: ScreenName.SwapSelectAccount, }, }); } else { diff --git a/apps/ledger-live-mobile/src/screens/WalletCentricAsset/index.tsx b/apps/ledger-live-mobile/src/screens/WalletCentricAsset/index.tsx index d47a2d1bd8ad..d147e35555b3 100644 --- a/apps/ledger-live-mobile/src/screens/WalletCentricAsset/index.tsx +++ b/apps/ledger-live-mobile/src/screens/WalletCentricAsset/index.tsx @@ -116,6 +116,7 @@ const AssetScreen = ({ route }: NavigationProps) => { params: { currency: currency.id, context: "addAccounts", + sourceScreenName: ScreenName.Asset, }, }); } else {