-
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 95bf4d2
Showing
16 changed files
with
351 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
14 changes: 14 additions & 0 deletions
14
apps/ledger-live-mobile/src/newArch/features/Reborn/__integrations__/shared.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,14 @@ | ||
import React from "react"; | ||
import { createStackNavigator } from "@react-navigation/stack"; | ||
import UpsellFlex from "../screens/UpsellFlex"; | ||
import { OnboardingContextProvider } from "~/screens/Onboarding/onboardingContext"; | ||
|
||
const Stack = createStackNavigator(); | ||
|
||
export const MockComponent = () => ( | ||
<OnboardingContextProvider> | ||
<Stack.Navigator> | ||
<Stack.Screen name="UpsellFlex" component={UpsellFlex} /> | ||
</Stack.Navigator> | ||
</OnboardingContextProvider> | ||
); |
29 changes: 29 additions & 0 deletions
29
...-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,29 @@ | ||
import React from "react"; | ||
import { render } from "@tests/test-renderer"; | ||
import { track } from "~/analytics"; | ||
import { MockComponent } from "./shared"; | ||
|
||
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.