Skip to content

Commit

Permalink
Harmonize headers and pixel polish LLM sync onboarding (#1486)
Browse files Browse the repository at this point in the history
* refactor(mobile): use unified header navigation in sync onboarding screens

* refactor(mobile): change the resync overlay into a gradient

* fix(mobile): remove useless padding in ble scanning

* refactor(mobile): rename the SyncOnboardingView and simplify it

* refactor(mobile): delete old sync onboarding view files

* fix: svg illustrations for device selection

* fix(mobile): update after rebase

* fix(mobile): remove rebase conflicts

* chore: generate changeset

* fix(mobile): remove rebase conflicts

Co-authored-by: Alexandre Magaud <[email protected]>
  • Loading branch information
thomasrogerlux and alexandremgo committed Oct 14, 2022
1 parent d5128ba commit 10984d0
Show file tree
Hide file tree
Showing 10 changed files with 359 additions and 402 deletions.
5 changes: 5 additions & 0 deletions .changeset/angry-buttons-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"live-mobile": patch
---

Small visual fixes on the new sync onboarding flow
79 changes: 79 additions & 0 deletions apps/ledger-live-mobile/src/components/DeviceSetupView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { ReactNode } from "react";
import { Flex } from "@ledgerhq/native-ui";
import { ArrowLeftMedium, CloseMedium } from "@ledgerhq/native-ui/assets/icons";
import { useNavigation } from "@react-navigation/native";
import { TouchableOpacity } from "react-native-gesture-handler";
import { SafeAreaView } from "react-native-safe-area-context";
import { useTheme } from "styled-components/native";

export type Props = {
hasBackButton?: boolean;
hasCloseButton?: boolean;
onBack?: () => void;
onClose?: () => void;
renderLeft?: () => ReactNode;
renderRight?: () => ReactNode;
children?: ReactNode;
};

const DeviceSetupView = ({
hasBackButton,
hasCloseButton,
onBack,
onClose,
renderLeft,
renderRight,
children,
}: Props) => {
const navigation = useNavigation();
const { colors } = useTheme();

const canGoBack = navigation.canGoBack();
const canRenderBackButton = (canGoBack && hasBackButton) || onBack;
const canRenderCloseButton = (canGoBack && hasCloseButton) || onClose;

const handleBack = () => {
navigation.goBack();
};

return (
// eslint-disable-next-line react-native/no-inline-styles
<SafeAreaView style={{ flex: 1, background: colors.background.main }}>
<Flex
px={6}
pt={8}
mb={7}
height={64}
flexDirection="row"
justifyContent="space-between"
alignItems="center"
>
{renderLeft ? (
renderLeft()
) : canRenderBackButton ? (
<Flex>
<TouchableOpacity onPress={onBack || handleBack}>
<ArrowLeftMedium size={24} />
</TouchableOpacity>
</Flex>
) : (
<Flex />
)}
{renderRight ? (
renderRight()
) : canRenderCloseButton ? (
<Flex>
<TouchableOpacity onPress={onClose || handleBack}>
<CloseMedium size={24} />
</TouchableOpacity>
</Flex>
) : (
<Flex />
)}
</Flex>
{children}
</SafeAreaView>
);
};

export default DeviceSetupView;
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ export default function BaseNavigator() {
component={BleDevicePairingFlow}
options={{
title: "",
headerShown: false,
}}
/>
<Stack.Screen
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useEffect } from "react";
import React, { ReactNode, useCallback, useEffect } from "react";
import { useTheme } from "styled-components/native";
import { useNavigation } from "@react-navigation/native";
import { useBleDevicePairing } from "@ledgerhq/live-common/ble/hooks/useBleDevicePairing";
import { Device } from "@ledgerhq/live-common/hw/actions/types";
import { useTranslation } from "react-i18next";
Expand All @@ -9,13 +8,11 @@ import { Flex, InfiniteLoader, Text, Button } from "@ledgerhq/native-ui";
import {
CircledCheckSolidMedium,
CircledCrossSolidMedium,
CloseMedium,
} from "@ledgerhq/native-ui/assets/icons";
import { TouchableOpacity } from "react-native-gesture-handler";

import Animation from "../../components/Animation";
import { getDeviceAnimation } from "../../helpers/getDeviceAnimation";
import type { BleDevicePairingFlowProps } from "./index";
import DeviceSetupView from "../../components/DeviceSetupView";

const TIMEOUT_AFTER_PAIRED_MS = 2000;

Expand All @@ -25,16 +22,6 @@ export type BleDevicePairingProps = {
deviceToPair: Device;
};

const HeaderRight = ({ onClose }: { onClose: () => void }) => {
return (
<Flex pt={8} px={6}>
<TouchableOpacity onPress={onClose}>
<CloseMedium size={24} />
</TouchableOpacity>
</Flex>
);
};

export const BleDevicePairing = ({
deviceToPair,
onPaired,
Expand All @@ -43,7 +30,6 @@ export const BleDevicePairing = ({
const { t } = useTranslation();
const { colors } = useTheme();
const theme = colors.type as "dark" | "light";
const navigation = useNavigation<BleDevicePairingFlowProps["navigation"]>();

const productName =
getDeviceModel(deviceToPair.modelId).productName || deviceToPair.modelId;
Expand All @@ -61,124 +47,101 @@ export const BleDevicePairing = ({
}
}, [isPaired, deviceToPair, onPaired]);

// Handles the update of the navigation header.
// In a useEffect as it cannot be put directly in the rendering function body
// because it updates the StackNavigator component
useEffect(() => {
const handleClose = useCallback(() => {
if (isPaired) {
// When paired, closing leads to onPaired
navigation.setOptions({
headerRight: () => (
<HeaderRight onClose={() => onPaired(deviceToPair)} />
),
headerLeft: () => <></>,
});
} else if (pairingError) {
// When error, closing leads to retry
navigation.setOptions({
headerRight: () => <HeaderRight onClose={onRetry} />,
headerLeft: () => <></>,
});
onPaired(deviceToPair);
} else {
// When pairing, closing leads to retry
navigation.setOptions({
headerRight: () => <HeaderRight onClose={onRetry} />,
headerLeft: () => <></>,
});
onRetry();
}
}, [deviceToPair, isPaired, navigation, onPaired, onRetry, pairingError]);
}, [deviceToPair, isPaired, onPaired, onRetry]);

if (isPaired) {
return (
<Flex bg="background.main" height="100%">
<Flex mt={10}>
<Flex alignItems="center">
<Flex
alignItems="center"
justifyContent="center"
p={1}
borderWidth={2}
borderRadius="9999px"
borderColor={colors.success.c80}
mb={6}
>
<CircledCheckSolidMedium color={colors.success.c80} size={48} />
</Flex>
<Text mb={8} textAlign="center" variant="h4" fontWeight="semiBold">
{t("blePairingFlow.pairing.success.title", {
deviceName,
})}
</Text>
let content: ReactNode;

<Animation
source={getDeviceAnimation({
device: deviceToPair,
key: "blePaired",
theme,
})}
/>
</Flex>
if (isPaired) {
content = (
<>
<Flex
alignItems="center"
justifyContent="center"
p={1}
borderWidth={2}
borderRadius="9999px"
borderColor={colors.success.c80}
mb={9}
>
<CircledCheckSolidMedium color={colors.success.c80} size={48} />
</Flex>
</Flex>
<Text mb={4} textAlign="center" variant="h4" fontWeight="semiBold">
{t("blePairingFlow.pairing.success.title", {
deviceName,
})}
</Text>
{/* Transparent text in order to have a smooth transition between loading and success */}
<Text variant="body" textAlign="center" mb={8} color="transparent">
{t("blePairingFlow.pairing.loading.subtitle", { productName })}
</Text>
<Animation
source={getDeviceAnimation({
device: deviceToPair,
key: "blePaired",
theme,
})}
/>
</>
);
}
if (pairingError) {
return (
<Flex bg="background.main" height="100%" justifyContent="space-between">
<Flex flex={1} mx="10" justifyContent="center" alignItems="center">
<Flex alignItems="center" justifyContent="center" p={5}>
} else if (pairingError) {
content = (
<Flex>
<Flex flex={1} alignItems="center" justifyContent="center">
<Flex alignItems="center" justifyContent="center" mb={8}>
<CircledCrossSolidMedium color={colors.error.c80} size={56} />
</Flex>
<Text mb={4} textAlign="center" variant="h4" fontWeight="semiBold">
{t("blePairingFlow.pairing.error.title")}
</Text>
<Text
variant="body"
fontWeight="medium"
mb={8}
color="neutral.c80"
textAlign="center"
>
<Text variant="body" mb={8} color="neutral.c80" textAlign="center">
{t("blePairingFlow.pairing.error.subtitle", { productName })}
</Text>
</Flex>
<Flex mb="10" mx="6">
<Button type="main" onPress={onRetry}>
{t("blePairingFlow.pairing.error.retryCta")}
</Button>
</Flex>
<Button type="main" onPress={onRetry} mb={8}>
{t("blePairingFlow.pairing.error.retryCta")}
</Button>
</Flex>
);
} else {
content = (
<>
<Flex mb={10}>
<InfiniteLoader color="primary.c80" size={48} />
</Flex>
<Text variant="h4" fontWeight="semiBold" textAlign="center" mb={4}>
{t("blePairingFlow.pairing.loading.title", { deviceName })}
</Text>
<Text variant="body" textAlign="center" mb={8} color="neutral.c80">
{t("blePairingFlow.pairing.loading.subtitle", { productName })}
</Text>
<Animation
source={getDeviceAnimation({
device: deviceToPair,
key: "blePairing",
theme,
})}
/>
</>
);
}

return (
<Flex bg="background.main" height="100%">
<Flex mt={10}>
<Flex alignItems="center">
<Flex mb={6} p={1} borderWidth={2} borderColor="transparent">
<InfiniteLoader size={48} />
</Flex>
<Text variant="h4" fontWeight="semiBold" textAlign="center" mb={4}>
{t("blePairingFlow.pairing.loading.title", { deviceName })}
</Text>
<Text
variant="body"
fontWeight="medium"
textAlign="center"
mb={8}
color="neutral.c80"
>
{t("blePairingFlow.pairing.loading.subtitle", { productName })}
</Text>
<Animation
source={getDeviceAnimation({
device: deviceToPair,
key: "blePairing",
theme,
})}
/>
</Flex>
<DeviceSetupView onClose={handleClose}>
<Flex
flex={1}
px={10}
pt={36}
justifyContent="center"
alignItems="center"
>
{content}
</Flex>
</Flex>
</DeviceSetupView>
);
};
Loading

0 comments on commit 10984d0

Please sign in to comment.