Skip to content

Commit ed0368f

Browse files
committed
feat(mobile): add tracking to walletv4 tour drawer
1 parent af87485 commit ed0368f

File tree

12 files changed

+114
-23
lines changed

12 files changed

+114
-23
lines changed

.changeset/quick-pugs-perform.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"live-mobile": minor
3+
---
4+
5+
feat: add trackings wallet v4 tour

apps/ledger-live-mobile/__tests__/jest-setup.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ jest.mock("~/analytics/segment", () => ({
143143
track: jest.fn(),
144144
setAnalyticsFeatureFlagMethod: jest.fn(),
145145
screen: jest.fn(),
146+
useTrack: jest.fn(() => jest.fn()),
146147
useAnalytics: jest.fn(() => ({
147148
track: jest.fn(),
148149
screen: jest.fn(),

apps/ledger-live-mobile/src/mvvm/features/Portfolio/screens/Portfolio/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ export const PortfolioScreen = ({ navigation }: NavigationProps) => {
6363

6464
const progressViewOffset = getProgressViewOffset(Platform.OS, shouldDisplayWallet40MainNav);
6565

66-
const { isDrawerOpen, handleCloseDrawer, slides } = useWalletV4TourDrawer();
66+
const { isDrawerOpen, handleCloseDrawer, closeDrawer, onSlideChange, slides } =
67+
useWalletV4TourDrawer();
6768

6869
const data = useMemo(() => {
6970
const sections: React.JSX.Element[] = [];
@@ -167,6 +168,8 @@ export const PortfolioScreen = ({ navigation }: NavigationProps) => {
167168
<WalletV4TourDrawer
168169
isDrawerOpen={isDrawerOpen}
169170
handleCloseDrawer={handleCloseDrawer}
171+
closeDrawer={closeDrawer}
172+
onSlideChange={onSlideChange}
170173
slides={slides}
171174
/>
172175
</>

apps/ledger-live-mobile/src/mvvm/features/WalletV4Tour/Debug/__tests__/WalletV4TourDebug.test.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ jest.mock("../../Drawer", () => ({
77
isDrawerOpen: false,
88
handleOpenDrawer: jest.fn(),
99
handleCloseDrawer: jest.fn(),
10+
closeDrawer: jest.fn(),
11+
onSlideChange: jest.fn(),
12+
slides: [],
1013
}),
1114
WalletV4TourDrawer: () => null,
1215
}));

apps/ledger-live-mobile/src/mvvm/features/WalletV4Tour/Debug/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ function WalletV4TourScreenDebug() {
1212
const dispatch = useDispatch();
1313

1414
const hasSeenTour = useSelector(hasSeenWalletV4TourSelector);
15-
const { isDrawerOpen, handleOpenDrawer, handleCloseDrawer, slides } = useWalletV4TourDrawer();
15+
const { isDrawerOpen, handleOpenDrawer, handleCloseDrawer, closeDrawer, onSlideChange, slides } =
16+
useWalletV4TourDrawer();
1617

1718
const handleToggleHasSeenTour = useCallback(() => {
1819
dispatch(setHasSeenWalletV4Tour(!hasSeenTour));
@@ -57,6 +58,8 @@ function WalletV4TourScreenDebug() {
5758
<WalletV4TourDrawer
5859
isDrawerOpen={isDrawerOpen}
5960
handleCloseDrawer={handleCloseDrawer}
61+
closeDrawer={closeDrawer}
62+
onSlideChange={onSlideChange}
6063
slides={slides}
6164
/>
6265
</View>

apps/ledger-live-mobile/src/mvvm/features/WalletV4Tour/Drawer/__integrations__/walletV4TourDrawer.test.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,17 @@ const SLIDES = [
2727
];
2828

2929
const TestComponent = () => {
30-
const { isDrawerOpen, handleOpenDrawer, handleCloseDrawer } = useWalletV4TourDrawer();
30+
const { isDrawerOpen, handleOpenDrawer, handleCloseDrawer, closeDrawer, onSlideChange } =
31+
useWalletV4TourDrawer();
3132

3233
return (
3334
<>
3435
<Button onPress={handleOpenDrawer} title="Open Drawer" />
3536
<WalletV4TourDrawer
3637
isDrawerOpen={isDrawerOpen}
3738
handleCloseDrawer={handleCloseDrawer}
39+
closeDrawer={closeDrawer}
40+
onSlideChange={onSlideChange}
3841
slides={SLIDES}
3942
/>
4043
</>

apps/ledger-live-mobile/src/mvvm/features/WalletV4Tour/Drawer/components/SlideFooterButton.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import React from "react";
22
import Animated, { interpolate, useAnimatedStyle } from "react-native-reanimated";
3-
import { useSlidesContext } from "@ledgerhq/native-ui";
43
import { useTranslation } from "~/context/Locale";
54
import { Button } from "@ledgerhq/lumen-ui-rnative";
65
import { useStyleSheet } from "@ledgerhq/lumen-ui-rnative/styles";
6+
import { useSlideFooterButtonViewModel } from "../hooks/useSlideFooterButtonViewModel";
77

88
interface SlideFooterButtonProps {
99
readonly onClose: () => void;
1010
}
1111

1212
export const SlideFooterButton = ({ onClose }: SlideFooterButtonProps) => {
13-
const { totalSlides, currentIndex, goToNext, scrollProgressSharedValue } = useSlidesContext();
13+
const { lastIndex, isLastSlide, scrollProgressSharedValue, goNext, complete } =
14+
useSlideFooterButtonViewModel(onClose);
1415

1516
const styles = useStyleSheet(
1617
() => ({
@@ -28,9 +29,7 @@ export const SlideFooterButton = ({ onClose }: SlideFooterButtonProps) => {
2829
);
2930
const { t } = useTranslation();
3031

31-
const lastIndex = totalSlides - 1;
3232
const fadeStart = lastIndex - 0.5;
33-
const isLastSlide = currentIndex >= lastIndex;
3433
const isInTest = process.env.NODE_ENV === "test";
3534

3635
const continueStyle = useAnimatedStyle(
@@ -63,7 +62,7 @@ export const SlideFooterButton = ({ onClose }: SlideFooterButtonProps) => {
6362
style={[styles.button, continueStyle]}
6463
pointerEvents={isLastSlide ? "none" : "box-none"}
6564
>
66-
<Button appearance="base" size="lg" onPress={goToNext}>
65+
<Button appearance="base" size="lg" onPress={goNext}>
6766
{t("walletV4Tour.cta.continue")}
6867
</Button>
6968
</Animated.View>
@@ -72,7 +71,7 @@ export const SlideFooterButton = ({ onClose }: SlideFooterButtonProps) => {
7271
style={[styles.button, exploreStyle]}
7372
pointerEvents={isLastSlide || isInTest ? "box-none" : "none"}
7473
>
75-
<Button appearance="base" size="lg" onPress={onClose}>
74+
<Button appearance="base" size="lg" onPress={complete}>
7675
{t("walletV4Tour.cta.explore")}
7776
</Button>
7877
</Animated.View>

apps/ledger-live-mobile/src/mvvm/features/WalletV4Tour/Drawer/components/SlideItem.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ export function SlideItem({ title, description, index, lottieSrc, speed }: Slide
121121
<Box
122122
lx={{
123123
flex: 1,
124-
marginBottom: "s40",
125124
}}
126125
>
127126
{shouldRender && isActive ? (
@@ -145,17 +144,24 @@ export function SlideItem({ title, description, index, lottieSrc, speed }: Slide
145144
</Box>
146145

147146
<Animated.View style={textAnimatedStyle} pointerEvents="none">
148-
<Text
149-
typography="heading2SemiBold"
147+
<Box
150148
lx={{
151-
textAlign: "center",
152-
color: "base",
153-
marginBottom: "s8",
149+
justifyContent: "center",
150+
minHeight: "s56", // the height of 2 lines of text
154151
}}
155-
numberOfLines={1}
156152
>
157-
{title}
158-
</Text>
153+
<Text
154+
typography="heading2SemiBold"
155+
lx={{
156+
textAlign: "center",
157+
color: "base",
158+
marginBottom: "s8",
159+
}}
160+
numberOfLines={2}
161+
>
162+
{title}
163+
</Text>
164+
</Box>
159165

160166
<Text
161167
typography="body2"
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { useCallback } from "react";
2+
import { useSlidesContext } from "@ledgerhq/native-ui";
3+
import { track } from "~/analytics";
4+
5+
export const useSlideFooterButtonViewModel = (onClose: () => void) => {
6+
const { totalSlides, currentIndex, goToNext, scrollProgressSharedValue } = useSlidesContext();
7+
8+
const lastIndex = totalSlides - 1;
9+
const isLastSlide = currentIndex >= lastIndex;
10+
11+
const goNext = useCallback(() => {
12+
goToNext();
13+
track("button_clicked", {
14+
button: "Next",
15+
page: "Product Tour WV4",
16+
card: currentIndex + 1,
17+
});
18+
}, [currentIndex, goToNext]);
19+
20+
const complete = useCallback(() => {
21+
onClose();
22+
track("button_clicked", {
23+
button: "Discover my new portfolio",
24+
page: "Product Tour WV4",
25+
});
26+
}, [onClose]);
27+
28+
return {
29+
lastIndex,
30+
isLastSlide,
31+
scrollProgressSharedValue,
32+
goNext,
33+
complete,
34+
};
35+
};

apps/ledger-live-mobile/src/mvvm/features/WalletV4Tour/Drawer/hooks/useWalletV4TourDrawerViewModel.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
import { useState, useCallback, useMemo } from "react";
1+
import { useState, useCallback, useMemo, useRef } from "react";
22
import { useDispatch, useSelector } from "~/context/hooks";
33
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
44
import { setHasSeenWalletV4Tour } from "~/actions/settings";
55
import { hasCompletedOnboardingSelector, hasSeenWalletV4TourSelector } from "~/reducers/settings";
66
import type { WalletV4TourDrawerViewModel } from "../types";
77
import { useTranslation } from "~/context/Locale";
8+
import { useTrack } from "~/analytics";
89
import animation01 from "../animations/01.lottie";
910
import animation02 from "../animations/02.lottie";
1011
import animation03 from "../animations/03.lottie";
1112

1213
export const useWalletV4TourDrawerViewModel = (): WalletV4TourDrawerViewModel => {
1314
const dispatch = useDispatch();
15+
const track = useTrack();
16+
const currentIndexRef = useRef(0);
1417
const hasSeenTour = useSelector(hasSeenWalletV4TourSelector);
1518
const hasCompletedOnboarding = useSelector(hasCompletedOnboardingSelector);
1619
const lwmWallet40 = useFeature("lwmWallet40");
@@ -21,8 +24,9 @@ export const useWalletV4TourDrawerViewModel = (): WalletV4TourDrawerViewModel =>
2124
const handleOpenDrawer = useCallback(() => {
2225
if (!hasCompletedOnboarding) {
2326
dispatch(setHasSeenWalletV4Tour(true));
24-
} else if (!hasSeenTour) setIsDrawerOpen(true);
25-
return;
27+
} else if (!hasSeenTour) {
28+
setIsDrawerOpen(true);
29+
}
2630
}, [hasCompletedOnboarding, hasSeenTour, dispatch]);
2731

2832
const handleCloseDrawer = useCallback(() => {
@@ -32,6 +36,26 @@ export const useWalletV4TourDrawerViewModel = (): WalletV4TourDrawerViewModel =>
3236
}
3337
}, [dispatch, hasSeenTour]);
3438

39+
const closeDrawer = useCallback(() => {
40+
track("button_clicked", {
41+
button: "Close",
42+
page: "Product Tour WV4",
43+
card: currentIndexRef.current + 1,
44+
});
45+
handleCloseDrawer();
46+
}, [handleCloseDrawer, track]);
47+
48+
const onSlideChange = useCallback(
49+
(index: number) => {
50+
currentIndexRef.current = index;
51+
track("product_tour_card", {
52+
page: "Product Tour WV4",
53+
card: index + 1,
54+
});
55+
},
56+
[track],
57+
);
58+
3559
const { t } = useTranslation();
3660
const slides = useMemo(
3761
() => [
@@ -61,6 +85,8 @@ export const useWalletV4TourDrawerViewModel = (): WalletV4TourDrawerViewModel =>
6185
isDrawerOpen,
6286
handleOpenDrawer,
6387
handleCloseDrawer,
88+
closeDrawer,
89+
onSlideChange,
6490
slides,
6591
};
6692
};

0 commit comments

Comments
 (0)