From e1bdf186773797dd3a87bd2e818362a4cb79cca2 Mon Sep 17 00:00:00 2001 From: Martin Cayuelas Date: Tue, 6 Aug 2024 09:37:42 +0200 Subject: [PATCH] [FEAT]: Add trustchain & createQRCodeHostInstance flow [FIX]: issue when staying long time on DisplayQRcode page Rework Hook --- .changeset/brave-lobsters-smoke.md | 5 + .../features/WalletSync/hooks/useQRCode.ts | 12 +- .../AddAccount/components/StepFlow.tsx | 70 ++++++------ .../Accounts/screens/AddAccount/index.tsx | 32 +++--- .../AddAccount/useAddAccountViewModel.ts | 50 +++++++- .../Accounts/types/enum/addAccount.ts | 5 - .../scanQRCode.integration.test.tsx | 16 +++ .../WalletSync/__integrations__/shared.tsx | 18 +++ ...synchronizeWithQrCode.integration.test.tsx | 46 ++++++++ .../walletSyncSettings.integration.test.tsx | 47 +------- .../components/Activation/ActivationFlow.tsx | 29 ++++- .../components/Synchronize/QrCode.tsx | 2 +- .../WalletSync/hooks/useQRCodeHost.ts | 107 ++++++++++++++++++ .../screens/Activation/ActivationDrawer.tsx | 6 + .../Activation/useActivationDrawerModel.ts | 23 +++- .../screens/Synchronize/QrCodeMethod.tsx | 14 ++- .../screens/Synchronize/SyncError.tsx | 21 ++++ .../features/WalletSync/types/Activation.ts | 5 + 18 files changed, 394 insertions(+), 114 deletions(-) create mode 100644 .changeset/brave-lobsters-smoke.md delete mode 100644 apps/ledger-live-mobile/src/newArch/features/Accounts/types/enum/addAccount.ts create mode 100644 apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/scanQRCode.integration.test.tsx create mode 100644 apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/synchronizeWithQrCode.integration.test.tsx create mode 100644 apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useQRCodeHost.ts create mode 100644 apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/SyncError.tsx diff --git a/.changeset/brave-lobsters-smoke.md b/.changeset/brave-lobsters-smoke.md new file mode 100644 index 000000000000..5fe98df2ba72 --- /dev/null +++ b/.changeset/brave-lobsters-smoke.md @@ -0,0 +1,5 @@ +--- +"live-mobile": patch +--- + +Add trustchain & createQRCodeHostInstance flow diff --git a/apps/ledger-live-desktop/src/newArch/features/WalletSync/hooks/useQRCode.ts b/apps/ledger-live-desktop/src/newArch/features/WalletSync/hooks/useQRCode.ts index 8060a274b69f..a1e09331b55b 100644 --- a/apps/ledger-live-desktop/src/newArch/features/WalletSync/hooks/useQRCode.ts +++ b/apps/ledger-live-desktop/src/newArch/features/WalletSync/hooks/useQRCode.ts @@ -29,13 +29,14 @@ export function useQRCode() { const startQRCodeProcessing = useCallback(() => { if (!trustchain || !memberCredentials) return; + let hasCompleted = false; + setError(null); setIsLoading(true); createQRCodeHostInstance({ trustchainApiBaseUrl, onDisplayQRCode: url => { setUrl(url); - setIsLoading(false); }, onDisplayDigits: digits => { dispatch(setQrCodePinCode(digits)); @@ -43,6 +44,7 @@ export function useQRCode() { }, addMember: async member => { await sdk.addMember(trustchain, memberCredentials, member); + hasCompleted = true; return trustchain; }, }) @@ -51,15 +53,17 @@ export function useQRCode() { return; } setError(e); - setIsLoading(false); }) .then(() => { + if (hasCompleted) dispatch(setFlow({ flow: Flow.Synchronize, step: Step.Synchronized })); + }) + .finally(() => { setUrl(null); dispatch(setQrCodePinCode(null)); setIsLoading(false); - dispatch(setFlow({ flow: Flow.Synchronize, step: Step.Synchronized })); + setError(null); }); - }, [trustchainApiBaseUrl, trustchain, memberCredentials, dispatch, sdk]); + }, [trustchain, memberCredentials, trustchainApiBaseUrl, dispatch, sdk]); return { url, diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/components/StepFlow.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/components/StepFlow.tsx index 86e57a449f6b..17440e1a354e 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/components/StepFlow.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/components/StepFlow.tsx @@ -1,54 +1,42 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React from "react"; import SelectAddAccountMethod from "./SelectAddAccountMethod"; import ChooseSyncMethod from "LLM/features/WalletSync/screens/Synchronize/ChooseMethod"; import QrCodeMethod from "LLM/features/WalletSync/screens/Synchronize/QrCodeMethod"; import { TrackScreen } from "~/analytics"; import { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets"; -import { Steps } from "../../../types/enum/addAccount"; import { AnalyticsPage } from "LLM/features/WalletSync/hooks/useLedgerSyncAnalytics"; +import { Options, Steps } from "LLM/features/WalletSync/types/Activation"; +import SyncError from "LLM/features/WalletSync/screens/Synchronize/SyncError"; +import PinCodeDisplay from "LLM/features/WalletSync/screens/Synchronize/PinCodeDisplay"; +import PinCodeInput from "LLM/features/WalletSync/screens/Synchronize/PinCodeInput"; type Props = { - startingStep: Steps; + currentStep: Steps; currency?: CryptoCurrency | TokenCurrency | null; doesNotHaveAccount?: boolean; - onStepChange?: (step: Steps) => void; - onGoBack?: (callback: () => void) => void; + setCurrentStep: (step: Steps) => void; + setCurrentOption: (option: Options) => void; + currentOption: Options; + navigateToChooseSyncMethod: () => void; + navigateToQrCodeMethod: () => void; + qrProcess: { + url: string | null; + error: Error | null; + isLoading: boolean; + pinCode: string | null; + }; }; const StepFlow = ({ - startingStep, doesNotHaveAccount, currency, - onGoBack, - onStepChange, + currentStep, + setCurrentOption, + currentOption, + navigateToChooseSyncMethod, + navigateToQrCodeMethod, + qrProcess, }: Props) => { - const [currentStep, setCurrentStep] = useState(startingStep); - - useEffect(() => { - if (onStepChange) onStepChange(currentStep); - }, [currentStep, onStepChange]); - - const navigateToChooseSyncMethod = () => setCurrentStep(Steps.ChooseSyncMethod); - const navigateToQrCodeMethod = () => setCurrentStep(Steps.QrCodeMethod); - - const getPreviousStep = useCallback( - (step: Steps): Steps => { - switch (step) { - case Steps.QrCodeMethod: - return Steps.ChooseSyncMethod; - case Steps.ChooseSyncMethod: - return Steps.AddAccountMethod; - default: - return startingStep; - } - }, - [startingStep], - ); - - useEffect(() => { - if (onGoBack) onGoBack(() => setCurrentStep(prevStep => getPreviousStep(prevStep))); - }, [getPreviousStep, onGoBack]); - const getScene = () => { switch (currentStep) { case Steps.AddAccountMethod: @@ -70,7 +58,17 @@ const StepFlow = ({ ); case Steps.QrCodeMethod: - return ; + return ; + + case Steps.PinDisplay: + return qrProcess.pinCode ? : null; + + case Steps.PinInput: + return ; + + case Steps.SyncError: + return ; + default: return null; } diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/index.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/index.tsx index e4b61c4aec18..99583a1f79c8 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/index.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/index.tsx @@ -1,11 +1,11 @@ -import React, { useState } from "react"; +import React from "react"; import useAddAccountViewModel from "./useAddAccountViewModel"; import QueuedDrawer from "~/components/QueuedDrawer"; import { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets"; import DrawerHeader from "LLM/features/WalletSync/components/Synchronize/DrawerHeader"; import { Flex } from "@ledgerhq/native-ui"; import StepFlow from "./components/StepFlow"; -import { Steps } from "../../types/enum/addAccount"; +import { Steps } from "LLM/features/WalletSync/types/Activation"; type ViewProps = ReturnType & AddAccountProps; @@ -16,37 +16,41 @@ type AddAccountProps = { onClose: () => void; }; -const StartingStep = Steps.AddAccountMethod; - function View({ isAddAccountDrawerVisible, doesNotHaveAccount, currency, onCloseAddAccountDrawer, + currentStep, + onGoBack, + currentOption, + setCurrentStep, + setCurrentOption, + navigateToChooseSyncMethod, + navigateToQrCodeMethod, + qrProcess, }: ViewProps) { - const [currentStep, setCurrentStep] = useState(StartingStep); - const CustomDrawerHeader = () => ; - const handleStepChange = (step: Steps) => setCurrentStep(step); - - let goBackCallback: () => void; - return ( goBackCallback()} + onBack={onGoBack} > (goBackCallback = callback)} + currentStep={currentStep} + setCurrentStep={setCurrentStep} + setCurrentOption={setCurrentOption} + currentOption={currentOption} + navigateToChooseSyncMethod={navigateToChooseSyncMethod} + navigateToQrCodeMethod={navigateToQrCodeMethod} + qrProcess={qrProcess} /> diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/useAddAccountViewModel.ts b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/useAddAccountViewModel.ts index b5c2dcad9417..1d744d049f15 100644 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/useAddAccountViewModel.ts +++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/useAddAccountViewModel.ts @@ -1,12 +1,40 @@ -import { useCallback } from "react"; +import { useCallback, useState } from "react"; import { track } from "~/analytics"; +import { useQRCodeHost } from "~/newArch/features/WalletSync/hooks/useQRCodeHost"; +import { Options, Steps } from "~/newArch/features/WalletSync/types/Activation"; type AddAccountDrawerProps = { isOpened: boolean; onClose: () => void; }; +const startingStep = Steps.AddAccountMethod; + const useAddAccountViewModel = ({ isOpened, onClose }: AddAccountDrawerProps) => { + const [currentStep, setCurrentStep] = useState(startingStep); + const [currentOption, setCurrentOption] = useState(Options.SCAN); + + const navigateToChooseSyncMethod = () => setCurrentStep(Steps.ChooseSyncMethod); + const navigateToQrCodeMethod = () => setCurrentStep(Steps.QrCodeMethod); + + const onGoBack = () => setCurrentStep(prevStep => getPreviousStep(prevStep)); + + const reset = () => { + setCurrentStep(startingStep); + setCurrentOption(Options.SCAN); + }; + + const getPreviousStep = useCallback((step: Steps): Steps => { + switch (step) { + case Steps.QrCodeMethod: + return Steps.ChooseSyncMethod; + case Steps.ChooseSyncMethod: + return Steps.AddAccountMethod; + default: + return startingStep; + } + }, []); + const trackButtonClick = useCallback((button: string) => { track("button_clicked", { button, @@ -17,11 +45,31 @@ const useAddAccountViewModel = ({ isOpened, onClose }: AddAccountDrawerProps) => const onCloseAddAccountDrawer = useCallback(() => { trackButtonClick("Close 'x'"); onClose(); + reset(); }, [trackButtonClick, onClose]); + const { url, error, isLoading, pinCode } = useQRCodeHost({ + setCurrentStep, + currentStep, + currentOption, + }); + return { isAddAccountDrawerVisible: isOpened, onCloseAddAccountDrawer, + navigateToQrCodeMethod, + navigateToChooseSyncMethod, + currentStep, + setCurrentOption, + currentOption, + setCurrentStep, + onGoBack, + qrProcess: { + url, + error, + isLoading, + pinCode, + }, }; }; diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/types/enum/addAccount.ts b/apps/ledger-live-mobile/src/newArch/features/Accounts/types/enum/addAccount.ts deleted file mode 100644 index 45b2587c4af4..000000000000 --- a/apps/ledger-live-mobile/src/newArch/features/Accounts/types/enum/addAccount.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Steps { - AddAccountMethod = "AddAccountMethod", - ChooseSyncMethod = "ChooseSyncMethod", - QrCodeMethod = "QrCodeMethod", -} diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/scanQRCode.integration.test.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/scanQRCode.integration.test.tsx new file mode 100644 index 000000000000..aabfaacda9ad --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/scanQRCode.integration.test.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import { render, screen } from "@tests/test-renderer"; + +import { INITIAL_TEST, WalletSyncSettingsNavigator } from "./shared"; + +describe("scanQRCode", () => { + it("Should open the QR code scene when 'scan a qr code' toggle is pressed", async () => { + const { user } = render(, { + overrideInitialState: INITIAL_TEST, + }); + await user.press(await screen.findByText(/ledger sync/i)); + await user.press(await screen.findByText(/already created a key?/i)); + await user.press(await screen.findByText(/scan a qr code/i)); + await expect(await screen.findByText(/show qr/i)).toBeVisible(); + }); +}); diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/shared.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/shared.tsx index a601c6e486c1..e84eebe9d236 100644 --- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/shared.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/shared.tsx @@ -8,6 +8,7 @@ import { WalletSyncNavigatorStackParamList } from "~/components/RootNavigator/ty import WalletSyncNavigator from "../WalletSyncNavigator"; import { BaseNavigatorStackParamList } from "~/components/RootNavigator/types/BaseNavigator"; import { QueryClientProvider, QueryClient } from "@tanstack/react-query"; +import { State } from "~/reducers/types"; const Stack = createStackNavigator< BaseNavigatorStackParamList & SettingsNavigatorStackParamList & WalletSyncNavigatorStackParamList @@ -41,3 +42,20 @@ export function WalletSyncSharedNavigator() { ); } + +export const INITIAL_TEST = (state: State) => ({ + ...state, + settings: { + ...state.settings, + readOnlyModeEnabled: false, + overriddenFeatureFlags: { + llmWalletSync: { + enabled: true, + params: { + environment: "STAGING", + watchConfig: {}, + }, + }, + }, + }, +}); diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/synchronizeWithQrCode.integration.test.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/synchronizeWithQrCode.integration.test.tsx new file mode 100644 index 000000000000..551ed8a4b23f --- /dev/null +++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/synchronizeWithQrCode.integration.test.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { render, screen, waitFor } from "@tests/test-renderer"; + +import { INITIAL_TEST, WalletSyncSettingsNavigator } from "./shared"; +import getWalletSyncEnvironmentParams from "@ledgerhq/live-common/walletSync/getEnvironmentParams"; + +jest.mock("@ledgerhq/trustchain/qrcode/index", () => ({ + createQRCodeHostInstance: () => ({ + trustchainApiBaseUrl: getWalletSyncEnvironmentParams("STAGING").trustchainApiBaseUrl, + onDisplayQRCode: jest.fn().mockImplementation(url => url), + onDisplayDigits: jest.fn().mockImplementation(digits => digits), + addMember: jest.fn(), + }), +})); + +describe("SynchronizeWithQrCode", () => { + it("Should display the QR code when 'show qr' toggle is pressed and add a new member through the flow", async () => { + const { user } = render(, { + overrideInitialState: INITIAL_TEST, + }); + await user.press(await screen.findByText(/ledger sync/i)); + await user.press(await screen.findByText(/already created a key?/i)); + await user.press(await screen.findByText(/scan a qr code/i)); + await user.press(await screen.findByText(/show qr/i)); + expect(await screen.getByTestId("ws-qr-code-displayed")).toBeVisible(); + + //PinCode Page after scanning QRCode + // Need to wait 3 seconds to simulate the time taken to scan the QR code + setTimeout(async () => { + await waitFor(() => { + expect(screen.getByText("Enter the code")).toBeDefined(); + }); + }, 3000); + + //Succes Page after PinCode + setTimeout(async () => { + await waitFor(() => { + expect( + screen.getByText( + "Changes in your accounts will now automatically appear across all apps and platforms.", + ), + ).toBeDefined(); + }); + }, 3000); + }); +}); diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncSettings.integration.test.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncSettings.integration.test.tsx index 4a6f8b27a710..7e54c9860f25 100644 --- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncSettings.integration.test.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncSettings.integration.test.tsx @@ -1,35 +1,17 @@ import React from "react"; import { screen } from "@testing-library/react-native"; import { render } from "@tests/test-renderer"; -import { WalletSyncSettingsNavigator } from "./shared"; -import { State } from "~/reducers/types"; +import { INITIAL_TEST, WalletSyncSettingsNavigator } from "./shared"; describe("WalletSyncSettings", () => { - const initialState = (state: State) => ({ - ...state, - settings: { - ...state.settings, - readOnlyModeEnabled: false, - overriddenFeatureFlags: { - llmWalletSync: { - enabled: true, - params: { - environment: "STAGING", - watchConfig: {}, - }, - }, - }, - }, - }); - it("Should display the ledger sync row", async () => { - render(, { overrideInitialState: initialState }); + render(, { overrideInitialState: INITIAL_TEST }); await expect(await screen.findByText(/ledger sync/i)).toBeVisible(); }); it("Should open the activation drawer when ledger sync row is pressed", async () => { const { user } = render(, { - overrideInitialState: initialState, + overrideInitialState: INITIAL_TEST, }); await user.press(await screen.findByText(/ledger sync/i)); await expect(await screen.findByText(/sync your accounts across all platforms/i)).toBeVisible(); @@ -38,31 +20,10 @@ describe("WalletSyncSettings", () => { it("Should open the drawer when 'already created a key' button is pressed", async () => { const { user } = render(, { - overrideInitialState: initialState, + overrideInitialState: INITIAL_TEST, }); await user.press(await screen.findByText(/ledger sync/i)); await user.press(await screen.findByText(/already created a key?/i)); await expect(await screen.findByText(/choose your sync method/i)).toBeVisible(); }); - - it("Should open the QR code scene when 'scan a qr code' toggle is pressed", async () => { - const { user } = render(, { - overrideInitialState: initialState, - }); - await user.press(await screen.findByText(/ledger sync/i)); - await user.press(await screen.findByText(/already created a key?/i)); - await user.press(await screen.findByText(/scan a qr code/i)); - await expect(await screen.findByText(/show qr/i)).toBeVisible(); - }); - - it("Should display the QR code when 'show qr' toggle is pressed", async () => { - const { user } = render(, { - overrideInitialState: initialState, - }); - await user.press(await screen.findByText(/ledger sync/i)); - await user.press(await screen.findByText(/already created a key?/i)); - await user.press(await screen.findByText(/scan a qr code/i)); - await user.press(await screen.findByText(/show qr/i)); - await expect(await screen.getByTestId("ws-show-qr-code")).toBeVisible(); - }); }); diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Activation/ActivationFlow.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Activation/ActivationFlow.tsx index efa95edf3f4d..64abaaa214a4 100644 --- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Activation/ActivationFlow.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Activation/ActivationFlow.tsx @@ -3,19 +3,34 @@ import Activation from "."; import { TrackScreen } from "~/analytics"; import ChooseSyncMethod from "../../screens/Synchronize/ChooseMethod"; import QrCodeMethod from "../../screens/Synchronize/QrCodeMethod"; -import { Steps } from "../../types/Activation"; +import { Options, Steps } from "../../types/Activation"; import { AnalyticsPage } from "../../hooks/useLedgerSyncAnalytics"; +import PinCodeDisplay from "../../screens/Synchronize/PinCodeDisplay"; +import PinCodeInput from "../../screens/Synchronize/PinCodeInput"; +import SyncError from "../../screens/Synchronize/SyncError"; type Props = { currentStep: Steps; navigateToChooseSyncMethod: () => void; navigateToQrCodeMethod: () => void; + qrProcess: { + url: string | null; + error: Error | null; + isLoading: boolean; + pinCode: string | null; + }; + + currentOption: Options; + setOption: (option: Options) => void; }; const ActivationFlow = ({ currentStep, navigateToChooseSyncMethod, navigateToQrCodeMethod, + qrProcess, + currentOption, + setOption, }: Props) => { const getScene = () => { switch (currentStep) { @@ -34,7 +49,17 @@ const ActivationFlow = ({ ); case Steps.QrCodeMethod: - return ; + return ; + + case Steps.PinDisplay: + return qrProcess.pinCode ? : null; + + case Steps.PinInput: + return ; + + case Steps.SyncError: + return ; + default: return null; } diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Synchronize/QrCode.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Synchronize/QrCode.tsx index a35fc9036c86..c8514b2daf92 100644 --- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Synchronize/QrCode.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Synchronize/QrCode.tsx @@ -76,7 +76,7 @@ const QrCode = ({ qrCodeValue }: Props) => { borderRadius={backgroundBorderRadius} background={colors.constant.white} justifyContent={"center"} - testID="ws-show-qr-code" + testID="ws-qr-code-displayed" > void; + currentStep: Steps; + currentOption: Options; +} + +export function useQRCodeHost({ setCurrentStep, currentStep, currentOption }: Props) { + const trustchain = useSelector(trustchainSelector); + const memberCredentials = useSelector(memberCredentialsSelector); + const sdk = useTrustchainSdk(); + + const featureWalletSync = useFeature("llmWalletSync"); + const { trustchainApiBaseUrl } = getWalletSyncEnvironmentParams( + featureWalletSync?.params?.environment, + ); + + const [isLoading, setIsLoading] = useState(false); + const [url, setUrl] = useState(null); + const [error, setError] = useState(null); + const [pinCode, setPinCode] = useState(null); + + const navigation = useNavigation(); + + const startQRCodeProcessing = useCallback(() => { + if (!trustchain || !memberCredentials || isLoading) return; + + let hasCompleted = false; + + setError(null); + setIsLoading(true); + createQRCodeHostInstance({ + trustchainApiBaseUrl, + onDisplayQRCode: url => { + setUrl(url); + + //TODO-remove when clearing code, used to test behavior with webTool + // eslint-disable-next-line no-console + console.log("onDisplayQRCode", url); + }, + onDisplayDigits: digits => { + setPinCode(digits); + setCurrentStep(Steps.PinDisplay); + }, + addMember: async member => { + await sdk.addMember(trustchain, memberCredentials, member); + hasCompleted = true; + return trustchain; + }, + }) + .catch(e => { + if (e instanceof InvalidDigitsError) { + setCurrentStep(Steps.SyncError); + return; + } + setError(e); + }) + .then(() => { + if (!error && hasCompleted) + navigation.navigate(NavigatorName.WalletSync, { + screen: ScreenName.WalletSyncSuccess, + params: { + created: false, + }, + }); + }) + .finally(() => { + setUrl(null); + setPinCode(null); + setIsLoading(false); + }); + }, [ + trustchain, + memberCredentials, + isLoading, + trustchainApiBaseUrl, + setCurrentStep, + sdk, + error, + navigation, + ]); + + useEffect(() => { + if (currentStep === Steps.QrCodeMethod && currentOption === Options.SHOW_QR) { + startQRCodeProcessing(); + } + }, [currentOption, currentStep, startQRCodeProcessing]); + + return { + url, + error, + isLoading, + startQRCodeProcessing, + pinCode, + }; +} diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/ActivationDrawer.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/ActivationDrawer.tsx index f65830b54c74..c1cf7aef4c56 100644 --- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/ActivationDrawer.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/ActivationDrawer.tsx @@ -26,6 +26,9 @@ function View({ goBackToPreviousStep, handleClose, onCloseDrawer, + qrProcess, + currentOption, + setCurrentOption, }: ViewProps) { const { height } = useWindowDimensions(); const maxDrawerHeight = height - 180; @@ -46,6 +49,9 @@ function View({ currentStep={currentStep} navigateToChooseSyncMethod={navigateToChooseSyncMethod} navigateToQrCodeMethod={navigateToQrCodeMethod} + qrProcess={qrProcess} + currentOption={currentOption} + setOption={setCurrentOption} /> diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/useActivationDrawerModel.ts b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/useActivationDrawerModel.ts index 408856bf9941..e9e35f6e6a93 100644 --- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/useActivationDrawerModel.ts +++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/useActivationDrawerModel.ts @@ -1,10 +1,12 @@ -import { useCallback, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; import { Steps } from "../../types/Activation"; import { AnalyticsButton, AnalyticsPage, useLedgerSyncAnalytics, } from "../../hooks/useLedgerSyncAnalytics"; +import { useQRCodeHost } from "../../hooks/useQRCodeHost"; +import { Options } from "LLM/features/WalletSync/types/Activation"; type Props = { isOpen: boolean; @@ -15,9 +17,13 @@ type Props = { const useActivationDrawerModel = ({ isOpen, startingStep, handleClose }: Props) => { const { onClickTrack } = useLedgerSyncAnalytics(); const [currentStep, setCurrentStep] = useState(startingStep); + const [currentOption, setCurrentOption] = useState(Options.SCAN); - const hasCustomHeader = currentStep === Steps.QrCodeMethod; - const canGoBack = currentStep === Steps.ChooseSyncMethod && startingStep === Steps.Activation; + const hasCustomHeader = useMemo(() => currentStep === Steps.QrCodeMethod, [currentStep]); + const canGoBack = useMemo( + () => currentStep === Steps.ChooseSyncMethod && startingStep === Steps.Activation, + [currentStep, startingStep], + ); const getPreviousStep = useCallback( (step: Steps): Steps => { @@ -44,13 +50,21 @@ const useActivationDrawerModel = ({ isOpen, startingStep, handleClose }: Props) }; const resetStep = () => setCurrentStep(startingStep); + const resetOption = () => setCurrentOption(Options.SCAN); const goBackToPreviousStep = () => setCurrentStep(getPreviousStep(currentStep)); const onCloseDrawer = () => { resetStep(); + resetOption(); handleClose(); }; + const { url, error, isLoading, pinCode } = useQRCodeHost({ + setCurrentStep, + currentStep, + currentOption, + }); + return { isOpen, currentStep, @@ -61,6 +75,9 @@ const useActivationDrawerModel = ({ isOpen, startingStep, handleClose }: Props) onCloseDrawer, handleClose, goBackToPreviousStep, + qrProcess: { url, error, isLoading, pinCode }, + currentOption, + setCurrentOption, }; }; diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/QrCodeMethod.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/QrCodeMethod.tsx index 40d64001a887..9834c98a6ce6 100644 --- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/QrCodeMethod.tsx +++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/QrCodeMethod.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React from "react"; import { Flex, TabSelector } from "@ledgerhq/native-ui"; import QrCode from "LLM/features/WalletSync/components/Synchronize/QrCode"; import { Options, OptionsType } from "LLM/features/WalletSync/types/Activation"; @@ -10,8 +10,12 @@ import { } from "../../hooks/useLedgerSyncAnalytics"; import { TrackScreen } from "~/analytics"; -const QrCodeMethod = () => { - const [selectedOption, setSelectedOption] = useState(Options.SCAN); +interface Props { + setSelectedOption: (option: OptionsType) => void; + currentOption: Options; +} + +const QrCodeMethod = ({ currentOption, setSelectedOption }: Props) => { const { onClickTrack } = useLedgerSyncAnalytics(); const { t } = useTranslation(); @@ -26,7 +30,7 @@ const QrCodeMethod = () => { }; const renderSwitch = () => { - switch (selectedOption) { + switch (currentOption) { case Options.SCAN: return ( <> @@ -55,7 +59,7 @@ const QrCodeMethod = () => { void; +} + +export default function SyncError({ tryAgain }: Props) { + const { t } = useTranslation(); + return ( + + ); +} diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/types/Activation.ts b/apps/ledger-live-mobile/src/newArch/features/WalletSync/types/Activation.ts index 8b0f6fb7368c..64867abb2b7e 100644 --- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/types/Activation.ts +++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/types/Activation.ts @@ -6,7 +6,12 @@ export enum Options { export type OptionsType = Options.SCAN | Options.SHOW_QR; export enum Steps { + AddAccountMethod = "AddAccountMethod", + Activation = "Activation", ChooseSyncMethod = "ChooseSyncMethod", QrCodeMethod = "QrCodeMethod", + PinDisplay = "PinDisplay", + PinInput = "PinInput", + SyncError = "SyncError", }