-
Notifications
You must be signed in to change notification settings - Fork 356
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat 💄(llm): update LNX drawer in Reborn
- Loading branch information
1 parent
38f7d5d
commit 85dea82
Showing
15 changed files
with
348 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"live-mobile": minor | ||
--- | ||
|
||
Create flex upsell drawer under ff to switch from the old LNX upsell drawer |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
...-live-mobile/src/newArch/features/Reborn/__integrations__/upSellFlex.integration.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import React from "react"; | ||
import { createStackNavigator } from "@react-navigation/stack"; | ||
import UpsellFlex from "../screens/UpsellFlex"; | ||
import { render } from "@tests/test-renderer"; | ||
import { OnboardingContextProvider } from "~/screens/Onboarding/onboardingContext"; | ||
import { track } from "~/analytics"; | ||
|
||
const Stack = createStackNavigator(); | ||
|
||
const MockComponent = () => ( | ||
<OnboardingContextProvider> | ||
<Stack.Navigator> | ||
<Stack.Screen name="UpsellFlex" component={UpsellFlex} /> | ||
</Stack.Navigator> | ||
</OnboardingContextProvider> | ||
); | ||
|
||
describe("UpsellFlex", () => { | ||
it("Should render UpsellFlex", async () => { | ||
const { getByText } = render(<MockComponent />); | ||
expect(getByText(/you need a ledger/i)).toBeVisible(); | ||
expect(getByText(/buy your ledger now/i)).toBeVisible(); | ||
expect(getByText(/i already have a ledger, set it up/i)).toBeVisible(); | ||
}); | ||
}); | ||
|
||
it("Should call tracking correctly", async () => { | ||
const { user, getByText } = render(<MockComponent />); | ||
await user.press(getByText(/i already have a ledger, set it up/i)); | ||
expect(track).toHaveBeenCalledWith("message_clicked", { | ||
message: "I already have a device, set it up now", | ||
page: "Upsell Flex", | ||
}); | ||
|
||
await user.press(getByText(/buy your ledger now/i)); | ||
expect(track).toHaveBeenCalledWith("message_clicked", { | ||
message: "I already have a device, set it up now", | ||
page: "Upsell Flex", | ||
}); | ||
}); |
186 changes: 186 additions & 0 deletions
186
apps/ledger-live-mobile/src/newArch/features/Reborn/screens/UpsellFlex/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
import React from "react"; | ||
import useUpsellFlexModel from "./useUpsellFlexModel"; | ||
import { | ||
Box, | ||
Button, | ||
Flex, | ||
IconBoxList, | ||
Icons, | ||
ScrollListContainer, | ||
Text, | ||
} from "@ledgerhq/native-ui"; | ||
import { TouchableOpacity } from "react-native"; | ||
import styled from "styled-components/native"; | ||
import videoSources from "../../../../../../assets/videos"; | ||
import Video from "react-native-video"; | ||
import GradientContainer from "~/components/GradientContainer"; | ||
import { TrackScreen } from "~/analytics"; | ||
|
||
const videoSource = videoSources.flex; | ||
|
||
const hitSlop = { | ||
bottom: 10, | ||
left: 24, | ||
right: 24, | ||
top: 10, | ||
}; | ||
|
||
const StyledSafeAreaView = styled(Box)` | ||
flex: 1; | ||
background-color: ${p => p.theme.colors.background.default}; | ||
padding-top: ${p => p.theme.space[10]}px; | ||
`; | ||
|
||
const CloseButton = styled(TouchableOpacity)` | ||
background-color: ${p => p.theme.colors.neutral.c30}; | ||
padding: 8px; | ||
border-radius: 32px; | ||
`; | ||
|
||
const items = [ | ||
{ | ||
title: "buyDevice.0.title", | ||
desc: "buyDevice.0.desc", | ||
Icon: Icons.Coins, | ||
}, | ||
{ | ||
title: "buyDevice.1.title", | ||
desc: "buyDevice.1.desc", | ||
Icon: Icons.GraphAsc, | ||
}, | ||
{ | ||
title: "buyDevice.2.title", | ||
desc: "buyDevice.2.desc", | ||
Icon: Icons.Globe, | ||
}, | ||
{ | ||
title: "buyDevice.3.title", | ||
desc: "buyDevice.3.desc", | ||
Icon: Icons.Flex, | ||
}, | ||
]; | ||
|
||
const videoStyle = { | ||
height: "100%", | ||
}; | ||
|
||
type ViewProps = ReturnType<typeof useUpsellFlexModel>; | ||
|
||
function View({ | ||
t, | ||
handleBack, | ||
setupDevice, | ||
buyLedger, | ||
colors, | ||
readOnlyModeEnabled, | ||
videoMounted, | ||
}: ViewProps) { | ||
return ( | ||
<StyledSafeAreaView> | ||
{readOnlyModeEnabled ? <TrackScreen category="ReadOnly" name="Upsell Flex" /> : null} | ||
<Flex | ||
flexDirection="row" | ||
alignItems="center" | ||
justifyContent="flex-end" | ||
width="100%" | ||
position="absolute" | ||
zIndex={10} | ||
p={6} | ||
top={50} | ||
> | ||
<CloseButton onPress={handleBack} hitSlop={hitSlop}> | ||
<Icons.Close size="S" /> | ||
</CloseButton> | ||
</Flex> | ||
<ScrollListContainer> | ||
<Flex | ||
height={320} | ||
borderTopLeftRadius={32} | ||
borderTopRightRadius={32} | ||
width="100%" | ||
overflow="hidden" | ||
opacity={videoMounted ? 0.8 : 0} | ||
> | ||
{videoMounted && ( | ||
<Video | ||
disableFocus | ||
source={videoSource} | ||
style={{ | ||
backgroundColor: colors.background.main, | ||
transform: [{ scale: 1.4 }], | ||
...(videoStyle as object), | ||
}} | ||
muted | ||
repeat={true} | ||
/> | ||
)} | ||
<GradientContainer | ||
color={colors.background.drawer} | ||
startOpacity={0} | ||
endOpacity={1} | ||
direction="top-to-bottom" | ||
containerStyle={{ | ||
position: "absolute", | ||
borderRadius: 0, | ||
left: 0, | ||
bottom: 0, | ||
width: "100%", | ||
height: "30%", | ||
}} | ||
/> | ||
</Flex> | ||
<Flex p={6}> | ||
<Text variant="h4" textAlign="center" lineHeight="32.4px"> | ||
{t("buyDevice.title")} | ||
</Text> | ||
<Flex mt={6} mb={8} justifyContent="center" alignItems="stretch"> | ||
<Text px={6} textAlign="center" variant="body" color="neutral.c70"> | ||
{t("buyDevice.desc")} | ||
</Text> | ||
</Flex> | ||
<IconBoxList | ||
iconShapes="circle" | ||
itemContainerProps={{ pr: 6 }} | ||
items={items.map(item => ({ | ||
Icon: <item.Icon size="S" />, | ||
title: t(item.title), | ||
description: ( | ||
<Text variant="paragraphLineHeight" color="neutral.c70"> | ||
{t(item.desc)} | ||
</Text> | ||
), | ||
}))} | ||
/> | ||
</Flex> | ||
</ScrollListContainer> | ||
<Flex> | ||
<Button | ||
mx={6} | ||
my={6} | ||
type="main" | ||
outline={false} | ||
testID="getDevice-buy-button" | ||
onPress={buyLedger} | ||
size="large" | ||
> | ||
{t("buyDevice.cta")} | ||
</Button> | ||
<Flex px={6} pt={0} mb={8}> | ||
<Button | ||
type="default" | ||
border={1} | ||
borderColor="neutral.c50" | ||
onPress={setupDevice} | ||
size="large" | ||
> | ||
{t("buyDevice.footer")} | ||
</Button> | ||
</Flex> | ||
</Flex> | ||
</StyledSafeAreaView> | ||
); | ||
} | ||
|
||
const UpsellFlex = () => <View {...useUpsellFlexModel()} />; | ||
|
||
export default UpsellFlex; |
96 changes: 96 additions & 0 deletions
96
apps/ledger-live-mobile/src/newArch/features/Reborn/screens/UpsellFlex/useUpsellFlexModel.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; | ||
import { useNavigation } from "@react-navigation/native"; | ||
import { useCallback } from "react"; | ||
import { useTranslation } from "react-i18next"; | ||
import { Linking } from "react-native"; | ||
import { useSelector, useDispatch } from "react-redux"; | ||
import { useTheme } from "styled-components/native"; | ||
import { setOnboardingHasDevice } from "~/actions/settings"; | ||
import { track } from "~/analytics"; | ||
import { BuyDeviceNavigatorParamList } from "~/components/RootNavigator/types/BuyDeviceNavigator"; | ||
import { | ||
BaseNavigationComposite, | ||
StackNavigatorNavigation, | ||
} from "~/components/RootNavigator/types/helpers"; | ||
import { OnboardingNavigatorParamList } from "~/components/RootNavigator/types/OnboardingNavigator"; | ||
import useIsAppInBackground from "~/components/useIsAppInBackground"; | ||
import { ScreenName, NavigatorName } from "~/const"; | ||
import { readOnlyModeEnabledSelector } from "~/reducers/settings"; | ||
import { useNavigationInterceptor } from "~/screens/Onboarding/onboardingContext"; | ||
import { urls } from "~/utils/urls"; | ||
|
||
type NavigationProp = BaseNavigationComposite< | ||
| StackNavigatorNavigation<BuyDeviceNavigatorParamList, ScreenName.GetDevice> | ||
| StackNavigatorNavigation<OnboardingNavigatorParamList, ScreenName.GetDevice> | ||
>; | ||
|
||
const useUpsellFlexModel = () => { | ||
const { t } = useTranslation(); | ||
const navigation = useNavigation<NavigationProp>(); | ||
const { colors } = useTheme(); | ||
const { setShowWelcome, setFirstTimeOnboarding } = useNavigationInterceptor(); | ||
const buyDeviceFromLive = useFeature("buyDeviceFromLive"); | ||
const readOnlyModeEnabled = useSelector(readOnlyModeEnabledSelector); | ||
const dispatch = useDispatch(); | ||
const currentNavigation = navigation.getParent()?.getParent()?.getState().routes[0].name; | ||
const isInOnboarding = currentNavigation === NavigatorName.BaseOnboarding; | ||
|
||
const handleBack = useCallback(() => { | ||
navigation.goBack(); | ||
if (readOnlyModeEnabled) { | ||
track("button_clicked", { | ||
button: "close", | ||
page: "Upsell Flex", | ||
}); | ||
} | ||
}, [readOnlyModeEnabled, navigation]); | ||
|
||
const setupDevice = useCallback(() => { | ||
setShowWelcome(false); | ||
setFirstTimeOnboarding(false); | ||
if (isInOnboarding) dispatch(setOnboardingHasDevice(true)); | ||
navigation.navigate(NavigatorName.BaseOnboarding, { | ||
screen: NavigatorName.Onboarding, | ||
params: { | ||
screen: ScreenName.OnboardingDeviceSelection, | ||
}, | ||
}); | ||
if (readOnlyModeEnabled) { | ||
track("message_clicked", { | ||
message: "I already have a device, set it up now", | ||
page: "Upsell Flex", | ||
}); | ||
} | ||
}, [ | ||
setShowWelcome, | ||
setFirstTimeOnboarding, | ||
isInOnboarding, | ||
dispatch, | ||
navigation, | ||
readOnlyModeEnabled, | ||
]); | ||
|
||
const buyLedger = useCallback(() => { | ||
if (buyDeviceFromLive?.enabled) { | ||
// FIXME: ScreenName.PurchaseDevice does not exist when coming from the Onboarding navigator | ||
// @ts-expect-error This seem very impossible to type because ts is right… | ||
navigation.navigate(ScreenName.PurchaseDevice); | ||
} else { | ||
Linking.openURL(urls.buyFlex); | ||
} | ||
}, [buyDeviceFromLive?.enabled, navigation]); | ||
|
||
const videoMounted = !useIsAppInBackground(); | ||
|
||
return { | ||
t, | ||
handleBack, | ||
setupDevice, | ||
buyLedger, | ||
colors, | ||
readOnlyModeEnabled, | ||
videoMounted, | ||
}; | ||
}; | ||
|
||
export default useUpsellFlexModel; |
Oops, something went wrong.