Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: πŸ” device selection after aborted flow #7778

Merged
merged 4 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lemon-starfishes-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"live-mobile": patch
---

Fix device selection after LedgerSync aborted flow
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SyncSkipUnderPriority } from "@ledgerhq/live-common/bridge/react/index";
import { Action, Device } from "@ledgerhq/live-common/hw/actions/types";
import { Alert, Flex } from "@ledgerhq/native-ui";
import React, { useCallback, useState } from "react";
import React, { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import styled from "styled-components/native";
import { PartialNullable } from "~/types/helpers";
Expand All @@ -23,6 +23,7 @@ type Props<Req, Stt, Res> = {
renderOnResult?: (_: Res) => JSX.Element | null;
onSelectDeviceLink?: () => void;
analyticsPropertyFlow?: string;
registerDeviceSelection?: (onDeviceUpdated: () => void) => void;
};

export default function DeviceActionModal<Req, Stt, Res>({
Expand All @@ -36,6 +37,7 @@ export default function DeviceActionModal<Req, Stt, Res>({
onModalHide,
onSelectDeviceLink,
analyticsPropertyFlow,
registerDeviceSelection,
}: Props<Req, Stt, Res>) {
const { t } = useTranslation();
const showAlert = !device?.wired;
Expand All @@ -54,6 +56,10 @@ export default function DeviceActionModal<Req, Stt, Res>({
}
}, [onClose, result]);

useEffect(() => {
registerDeviceSelection?.(() => setResult(null));
}, [registerDeviceSelection]);

return (
<QueuedDrawer
isRequestingToBeOpened={result ? false : !!device}
Expand Down
18 changes: 17 additions & 1 deletion apps/ledger-live-mobile/src/hooks/deviceActions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo } from "react";
import { useCallback, useMemo, useRef, useState } from "react";
import { createAction as appCreateAction } from "@ledgerhq/live-common/hw/actions/app";
import { createAction as transactionCreateAction } from "@ledgerhq/live-common/hw/actions/transaction";
import { createAction as startExchangeCreateAction } from "@ledgerhq/live-common/hw/actions/startExchange";
Expand All @@ -11,6 +11,7 @@ import { createAction as staxFetchImageCreateAction } from "@ledgerhq/live-commo
import { createAction as installLanguageCreateAction } from "@ledgerhq/live-common/hw/actions/installLanguage";
import { createAction as staxRemoveImageCreateAction } from "@ledgerhq/live-common/hw/actions/customLockScreenRemove";
import { createAction as renameDeviceCreateAction } from "@ledgerhq/live-common/hw/actions/renameDevice";
import type { Device } from "@ledgerhq/live-common/hw/actions/types";
import renameDevice from "@ledgerhq/live-common/hw/renameDevice";
import customLockScreenLoad from "@ledgerhq/live-common/hw/customLockScreenLoad";
import installLanguage from "@ledgerhq/live-common/hw/installLanguage";
Expand Down Expand Up @@ -124,3 +125,18 @@ export function useRenameDeviceAction() {
[mock],
);
}

export function useSelectDevice() {
const [device, setDevice] = useState<Device | null | undefined>(null);

const onDeviceUpdated = useRef<() => void>();
const registerDeviceSelection = useCallback((handler: () => void) => {
onDeviceUpdated.current = handler;
}, []);
const selectDevice = useCallback((device: Device | null | undefined) => {
setDevice(device);
onDeviceUpdated.current?.();
}, []);

return { device, selectDevice, registerDeviceSelection };
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
ReactNavigationHeaderOptions,
StackNavigatorProps,
} from "~/components/RootNavigator/types/helpers";
import { useAppDeviceAction } from "~/hooks/deviceActions";
import { useAppDeviceAction, useSelectDevice } from "~/hooks/deviceActions";
import { AppResult } from "@ledgerhq/live-common/hw/actions/app";
import { WalletSyncNavigatorStackParamList } from "~/components/RootNavigator/types/WalletSyncNavigator";
import { TRUSTCHAIN_APP_NAME } from "@ledgerhq/hw-trustchain";
Expand Down Expand Up @@ -47,16 +47,12 @@ const WalletSyncActivationDeviceSelection: React.FC<ChooseDeviceProps> = ({
}) => {
const isFocused = useIsFocused();
const action = useAppDeviceAction();
const [device, setDevice] = useState<Device | null>();
const { device, selectDevice, registerDeviceSelection } = useSelectDevice();
const [isHeaderOverridden, setIsHeaderOverridden] = useState<boolean>(false);

const navigation = useNavigation<NavigationProps["navigation"]>();

const onSelectDevice = useCallback((device: Device) => {
setDevice(device);
}, []);

const onClose = () => setDevice(null);
const onClose = () => selectDevice(null);

const onResult = useCallback(
(payload: AppResult) => {
Expand All @@ -71,7 +67,7 @@ const WalletSyncActivationDeviceSelection: React.FC<ChooseDeviceProps> = ({
// and avoids a duplicated error drawers/messages.
// The only drawback: the user has to select again their device once the bluetooth requirements are respected.
if (error instanceof BluetoothRequired) {
setDevice(undefined);
selectDevice(undefined);
themooneer marked this conversation as resolved.
Show resolved Hide resolved
}
};

Expand Down Expand Up @@ -116,7 +112,7 @@ const WalletSyncActivationDeviceSelection: React.FC<ChooseDeviceProps> = ({
) : null}
<Flex flex={1} mb={8}>
<SelectDevice2
onSelect={onSelectDevice}
onSelect={selectDevice}
stopBleScanning={!!device || !isFocused}
requestToSetHeaderOptions={requestToSetHeaderOptions}
/>
Expand All @@ -128,6 +124,7 @@ const WalletSyncActivationDeviceSelection: React.FC<ChooseDeviceProps> = ({
action={action}
request={request}
onError={onError}
registerDeviceSelection={registerDeviceSelection}
/>
</SafeAreaView>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useCallback, useEffect, useState } from "react";
import React, { useCallback, useEffect } from "react";
import { StyleSheet, SafeAreaView } from "react-native";
import type { Device } from "@ledgerhq/live-common/hw/actions/types";
import { useIsFocused, useTheme } from "@react-navigation/native";
import { isTokenCurrency } from "@ledgerhq/live-common/currencies/index";
import { Flex } from "@ledgerhq/native-ui";
Expand All @@ -16,7 +15,7 @@ import type { AddAccountsNavigatorParamList } from "~/components/RootNavigator/t
import SkipSelectDevice from "../SkipSelectDevice";
import AddAccountsHeaderRightClose from "./AddAccountsHeaderRightClose";
import { NavigationHeaderBackButton } from "~/components/NavigationHeaderBackButton";
import { useAppDeviceAction } from "~/hooks/deviceActions";
import { useAppDeviceAction, useSelectDevice } from "~/hooks/deviceActions";

type Props = StackNavigatorProps<AddAccountsNavigatorParamList, ScreenName.AddAccountsSelectDevice>;

Expand All @@ -32,17 +31,17 @@ export default function AddAccountsSelectDevice({ navigation, route }: Props) {
const action = useAppDeviceAction();
const { currency, analyticsPropertyFlow } = route.params;
const { colors } = useTheme();
const [device, setDevice] = useState<Device | null | undefined>(null);
const { device, selectDevice, registerDeviceSelection } = useSelectDevice();
const isFocused = useIsFocused();

const onClose = useCallback(() => {
setDevice(null);
}, []);
selectDevice(null);
}, [selectDevice]);

const onResult = useCallback(
// @ts-expect-error should be AppResult but navigation.navigate does not agree
meta => {
setDevice(null);
selectDevice(null);
const { inline } = route.params;
const arg = { ...route.params, ...meta };

Expand All @@ -52,7 +51,7 @@ export default function AddAccountsSelectDevice({ navigation, route }: Props) {
navigation.navigate(ScreenName.AddAccountsAccounts, arg);
}
},
[navigation, route],
[selectDevice, navigation, route],
);

useEffect(() => {
Expand Down Expand Up @@ -89,10 +88,10 @@ export default function AddAccountsSelectDevice({ navigation, route }: Props) {
},
]}
>
<SkipSelectDevice route={route} onResult={setDevice} />
<SkipSelectDevice route={route} onResult={selectDevice} />
<Flex px={16} py={8} flex={1}>
<SelectDevice2
onSelect={setDevice}
onSelect={selectDevice}
stopBleScanning={!!device || !isFocused}
requestToSetHeaderOptions={requestToSetHeaderOptions}
/>
Expand All @@ -105,8 +104,9 @@ export default function AddAccountsSelectDevice({ navigation, route }: Props) {
request={{
currency: currency && isTokenCurrency(currency) ? currency.parentCurrency : currency,
}}
onSelectDeviceLink={() => setDevice(null)}
onSelectDeviceLink={() => selectDevice(null)}
analyticsPropertyFlow={analyticsPropertyFlow || "add account"}
registerDeviceSelection={registerDeviceSelection}
/>
</SafeAreaView>
);
Expand Down
Loading