Skip to content

Commit

Permalink
Bugfix/live 6578 new ble flow screen (#4877)
Browse files Browse the repository at this point in the history
* fix(blePairing): create blePairing screen and navigate to it

* fix: replace debug settings screen with debug features

* fix: add debug ble pairing screen using generic ble component

* refactor(blePainrig): use BleDevicePairingFlow screen for deep link only

* fix(onboarding): deep link stax ble pairing

* fix(review): naming

* fix(review): typings screen params

---------

Co-authored-by: Louis Aussedat <louis.aussedat@ledger.fr>
  • Loading branch information
jdabbech-ledger and aussedatlo authored Oct 10, 2023
1 parent 9d8c602 commit bb247ac
Show file tree
Hide file tree
Showing 15 changed files with 213 additions and 203 deletions.
5 changes: 5 additions & 0 deletions .changeset/empty-taxis-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"live-mobile": patch
---

improve ble pairing deep links
5 changes: 5 additions & 0 deletions .changeset/thin-news-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"live-mobile": patch
---

Create new bluetooth pairing flow screen for onboarding
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import OnboardingNewDevice from "../../screens/Onboarding/steps/setupDevice";
import OnboardingRecoveryPhrase from "../../screens/Onboarding/steps/recoveryPhrase";
import OnboardingInfoModal from "../OnboardingStepperView/OnboardingInfoModal";

import OnboardingBleDevicePairingFlow from "../../screens/Onboarding/steps/BleDevicePairingFlow";
import OnboardingPairNew from "../../screens/Onboarding/steps/pairNew";
import OnboardingImportAccounts from "../../screens/Onboarding/steps/importAccounts";
import OnboardingPreQuizModal from "../../screens/Onboarding/steps/setupDevice/drawers/OnboardingPreQuizModal";
Expand Down Expand Up @@ -149,6 +150,14 @@ export default function OnboardingNavigator() {
headerLeft: () => <NavigationHeaderBackButton />,
}}
/>
<Stack.Screen
name={ScreenName.OnboardingBleDevicePairingFlow}
component={OnboardingBleDevicePairingFlow}
options={{
headerShown: true,
headerLeft: () => <NavigationHeaderBackButton />,
}}
/>
<Stack.Screen
name={ScreenName.OnboardingUseCase}
component={OnboardingUseCase}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import { SettingsNavigatorStackParamList } from "./types/SettingsNavigator";
import DebugTermsOfUse from "../../screens/Settings/Debug/Features/TermsOfUse";
import CameraPermissions from "../../screens/Settings/Debug/Debugging/CameraPermissions";
import DebugQueuedDrawers from "../../screens/Settings/Debug/Features/QueuedDrawers";
import BleEDevicePairingScreen from "../../screens/Settings/Debug/Features/BleDevicePairingScreen";

const Stack = createStackNavigator<SettingsNavigatorStackParamList>();

Expand Down Expand Up @@ -300,6 +301,13 @@ export default function SettingsNavigator() {
),
})}
/>
<Stack.Screen
name={ScreenName.DebugBLEDevicePairing}
component={BleEDevicePairingScreen}
options={{
title: "Debug Ble Pairing Flow",
}}
/>
<Stack.Screen
name={ScreenName.DebugCommandSender}
component={DebugCommandSender}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,17 +166,7 @@ export type BaseNavigatorStackParamList = {
transaction?: Transaction;
justScanned?: boolean;
};
[ScreenName.BleDevicePairingFlow]: {
filterByDeviceModelId?: DeviceModelId;
areKnownDevicesDisplayed?: boolean;
areKnownDevicesPairable?: boolean;
onSuccessAddToKnownDevices?: boolean;
onSuccessNavigateToConfig: {
navigateInput: NavigateInput;
pathToDeviceParam: PathToDeviceParam;
navigationType?: NavigationType;
};
};
[ScreenName.BleDevicePairingFlow]: undefined;
[ScreenName.AnalyticsAllocation]: undefined;
[ScreenName.AnalyticsOperations]: {
accountsIds: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ export type OnboardingNavigatorParamList = {
success: boolean;
deviceModelId: DeviceModelId;
};
[ScreenName.OnboardingBleDevicePairingFlow]: undefined;
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Device } from "@ledgerhq/types-devices";
import { ScreenName } from "../../../const";
import { FilterByDeviceModelId } from "../../BleDevicePairingFlow/BleDevicesScanning";

export type SettingsNavigatorStackParamList = {
[ScreenName.SettingsScreen]: undefined;
Expand All @@ -19,17 +20,17 @@ export type SettingsNavigatorStackParamList = {
[ScreenName.ExperimentalSettings]: undefined;
[ScreenName.DeveloperSettings]: undefined;
[ScreenName.DeveloperCustomManifest]: undefined;
[ScreenName.DebugSettings]:
| {
pairedDevice?: Device | null;
}
| undefined;
[ScreenName.DebugSettings]: undefined;
[ScreenName.DebugFeatureFlags]: undefined;
[ScreenName.DebugInformation]: undefined;
[ScreenName.DebugPerformance]: undefined;
[ScreenName.DebugDebugging]: undefined;
[ScreenName.DebugConfiguration]: undefined;
[ScreenName.DebugFeatures]: undefined;
[ScreenName.DebugFeatures]:
| {
pairedDevice?: Device | null;
}
| undefined;
[ScreenName.DebugConnectivity]: undefined;
[ScreenName.DebugGenerators]: undefined;
[ScreenName.DebugMockGenerateAccounts]: undefined;
Expand All @@ -45,6 +46,11 @@ export type SettingsNavigatorStackParamList = {
[ScreenName.DebugBLEBenchmark]: {
deviceId: string;
};
[ScreenName.DebugBLEDevicePairing]: {
areKnownDevicesDisplayed: boolean;
onSuccessAddToKnownDevices: boolean;
filterByDeviceModelId?: FilterByDeviceModelId;
};
[ScreenName.DebugCrash]: undefined;
[ScreenName.DebugStore]: undefined;
[ScreenName.DebugEnv]: undefined;
Expand Down
2 changes: 2 additions & 0 deletions apps/ledger-live-mobile/src/const/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export enum ScreenName {
CurrencySettings = "CurrencySettings",
DebugBLE = "DebugBLE",
DebugBLEBenchmark = "DebugBLEBenchmark",
DebugBLEDevicePairing = "DebugBLEDevicePairing",
DebugConfiguration = "DebugConfiguration",
DebugCommandSender = "DebugCommandSender",
DebugConnectivity = "DebugConnectivity",
Expand Down Expand Up @@ -397,6 +398,7 @@ export enum ScreenName {
OnboardingImportSelectAccount = "OnboardingImportSelectAccount",
OnboardingQuiz = "OnboardingQuiz",
OnboardingQuizFinal = "OnboardingQuizFinal",
OnboardingBleDevicePairingFlow = "OnboardingBleDevicePairingFlow",
OnboardingLanguageModal = "OnboardingLanguageModal",
OnboardingModalSetupNewDevice = "OnboardingModalSetupNewDevice",
OnboardingModalSetupSteps = "OnboardingModalSetupSteps",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useEffect, useState } from "react";
import { NavigationProp } from "@react-navigation/native";
import { NavigationProp, ParamListBase } from "@react-navigation/native";

// Returns a counter that increments itself when the component using it
// gets a new focus from the navigation mechanism
export function useIncrementOnNavigationFocusState<
NavigationType extends NavigationProp<Record<string, unknown>>,
NavigationType extends NavigationProp<ParamListBase>,
>(navigation: NavigationType): number {
const [nonce, setNonce] = useState<number>(0);
// Avoids forcing the rendering on the first focus on the screen.
Expand Down
11 changes: 10 additions & 1 deletion apps/ledger-live-mobile/src/navigation/DeeplinksProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -386,10 +386,19 @@ const getOnboardingLinkingOptions = (
screens: !acceptedTermsOfUse
? {}
: {
[NavigatorName.BaseOnboarding]: {
screens: {
[NavigatorName.Onboarding]: {
initialRouteName: ScreenName.OnboardingWelcome,
screens: {
[ScreenName.OnboardingBleDevicePairingFlow]: "sync-onboarding",
},
},
},
},
[NavigatorName.Base]: {
screens: {
[ScreenName.PostBuyDeviceScreen]: "hw-purchase-success",
[ScreenName.BleDevicePairingFlow]: "sync-onboarding",
/**
* @params ?platform: string
* ie: "ledgerlive://discover/protect?theme=light" will open the catalog and the protect dapp with a light theme as parameter
Expand Down
125 changes: 14 additions & 111 deletions apps/ledger-live-mobile/src/screens/BleDevicePairingFlow/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { Device, DeviceModelId } from "@ledgerhq/types-devices";
import React, { useCallback } from "react";
import { has as hasFromPath, set as setFromPath } from "lodash";
import { Flex } from "@ledgerhq/native-ui";
import { SafeAreaView } from "react-native-safe-area-context";
import { NavigatorName, ScreenName } from "../../const";
import { useIncrementOnNavigationFocusState } from "../../helpers/useIncrementOnNavigationFocusState";
import BleDevicePairingFlowComponent, {
SetHeaderOptionsRequest,
} from "../../components/BleDevicePairingFlow/index";
Expand All @@ -15,6 +13,7 @@ import {
} from "../../components/RootNavigator/types/helpers";
import { BaseNavigatorStackParamList } from "../../components/RootNavigator/types/BaseNavigator";
import { NavigationHeaderBackButton } from "../../components/NavigationHeaderBackButton";
import { useIncrementOnNavigationFocusState } from "../../helpers/useIncrementOnNavigationFocusState";

export type Props = RootComposite<
StackNavigatorProps<BaseNavigatorStackParamList, ScreenName.BleDevicePairingFlow>
Expand All @@ -28,118 +27,27 @@ export const bleDevicePairingFlowHeaderOptions: ReactNavigationHeaderOptions = {
headerRight: () => null,
};

// Necessary when the pairing flow is opened from a deeplink without any params
// Shouldn't be relied upon for other usages
const defaultNavigationParams = {
filterByDeviceModelId: DeviceModelId.stax, // This needs to be removed when nanos are supported
areKnownDevicesDisplayed: true,
onSuccessAddToKnownDevices: false,
successNavigateToConfig: {
navigationType: "navigate",
pathToDeviceParam: "params.params.params.device",
navigateInput: {
name: NavigatorName.BaseOnboarding,
params: {
screen: NavigatorName.SyncOnboarding,
params: {
screen: ScreenName.SyncOnboardingCompanion,
params: {
device: null,
},
},
},
},
},
};

/**
* Screen handling the BLE flow with a scanning step and a pairing step
*
* Note: this screen will disappear in the future, but still necessary for now.
* Note: this screen is only used from deep link sync-onboarding
*
* @param navigation react-navigation navigation object
* @param route react-navigation route object. The route params are:
* - filterByDeviceModelId: (optional, default to none) a device model id to filter on
* - areKnownDevicesDisplayed: boolean, display the already known device if true,
* filter out them if false (default to true)
* - onSuccessNavigateToConfig: object containing navigation config parameters when successful pairing:
* - navigateInput: navigation object given as input to navigation.navigate. 2 mandatory props:
* - name: navigator name or screen name if no need to specify a navigator
* - params: navigation params
* Ex for a nested navigation:
* {
* . name: NavigatorName.A_NAVIGATOR,
* params: {
* screen: ScreenName.A_SCREEN,
* params: {
* ...SOME_PARAMS,
* pairedDevice: null,
* },
* },
* }
* - pathToDeviceParam: path to device property that is nested into navigateInput
* From the ex of navigateInput, it would be: "params.params.pairedDevice"
* - navigationType: (optional, default to "navigate") when navigating after a successful pairing,
* choose between a "replace" or a "navigate"
* The default success config will navigate to the synchronous onboarding, however it shouldn't be
* relied upon and exist solely to simplify deeplinking to the sync onboarding.
* - onSuccessAddToKnownDevices: boolean, if true the successfully paired device is added to the redux
* list of known devices. Not added if false (default to false).
* @returns a JSX component
*/
export const BleDevicePairingFlow = ({ navigation, route }: Props) => {
const params = route?.params || defaultNavigationParams;

const {
filterByDeviceModelId = undefined,
areKnownDevicesDisplayed = true,
areKnownDevicesPairable = false,
onSuccessAddToKnownDevices = false,
onSuccessNavigateToConfig = defaultNavigationParams.successNavigateToConfig,
} = params;

const {
navigateInput,
pathToDeviceParam,
navigationType = "navigate",
} = onSuccessNavigateToConfig;

// Makes sure the pairing components are reset when navigating back to this screen
export const BleDevicePairingFlow: React.FC<Props> = ({ navigation }) => {
const keyToReset = useIncrementOnNavigationFocusState<Props["navigation"]>(navigation);

const onPairingSuccess = useCallback(
(device: Device) => {
const hasDeviceParam = hasFromPath(navigateInput, pathToDeviceParam);
if (hasDeviceParam) {
setFromPath(navigateInput, pathToDeviceParam, device);
} else {
console.error(
`BLE pairing flow: device path param ${String(
pathToDeviceParam,
)} not existing on navigation input`,
);
}

const params = navigateInput.params
? {
...navigateInput.params,
}
: undefined;

if (navigationType === "push") {
// @ts-expect-error this seems complicated to type properly
// the typings for react-navigation cannot reconciliate screens having "undefined" params and object params.
// And this will disappear in a future screen.
navigation.push(navigateInput.name, params);
} else if (navigationType === "replace") {
// @ts-expect-error this seems complicated to type properly
navigation.replace(navigateInput.name, params);
} else {
// @ts-expect-error this seems complicated to type properly
navigation.navigate(navigateInput.name, params);
}
// Navigates to the sync onboarding passing the newly paired device
navigation.navigate(NavigatorName.BaseOnboarding, {
screen: NavigatorName.SyncOnboarding,
params: {
screen: ScreenName.SyncOnboardingCompanion,
params: { device },
},
});
},
[navigateInput, navigation, navigationType, pathToDeviceParam],
[navigation],
);

const requestToSetHeaderOptions = useCallback(
Expand All @@ -151,9 +59,7 @@ export const BleDevicePairingFlow = ({ navigation, route }: Props) => {
});
} else {
// Sets back the header to its initial values set for this screen
navigation.setOptions({
...bleDevicePairingFlowHeaderOptions,
});
navigation.setOptions(bleDevicePairingFlowHeaderOptions);
}
},
[navigation],
Expand All @@ -164,11 +70,8 @@ export const BleDevicePairingFlow = ({ navigation, route }: Props) => {
<Flex px={6} flex={1}>
<BleDevicePairingFlowComponent
key={keyToReset}
filterByDeviceModelId={filterByDeviceModelId}
areKnownDevicesDisplayed={areKnownDevicesDisplayed}
areKnownDevicesPairable={areKnownDevicesPairable}
filterByDeviceModelId={DeviceModelId.stax}
onPairingSuccess={onPairingSuccess}
onPairingSuccessAddToKnownDevices={onSuccessAddToKnownDevices}
requestToSetHeaderOptions={requestToSetHeaderOptions}
/>
</Flex>
Expand Down
Loading

0 comments on commit bb247ac

Please sign in to comment.