Skip to content

Commit

Permalink
[FEAT]: Add trustchain & createQRCodeHostInstance flow
Browse files Browse the repository at this point in the history
[FIX]: issue when staying long time on DisplayQRcode page

Rework Hook
  • Loading branch information
mcayuelas-ledger committed Aug 7, 2024
1 parent 3de93cc commit e1bdf18
Show file tree
Hide file tree
Showing 18 changed files with 394 additions and 114 deletions.
5 changes: 5 additions & 0 deletions .changeset/brave-lobsters-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"live-mobile": patch
---

Add trustchain & createQRCodeHostInstance flow
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,22 @@ 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));
dispatch(setFlow({ flow: Flow.Synchronize, step: Step.PinCode }));
},
addMember: async member => {
await sdk.addMember(trustchain, memberCredentials, member);
hasCompleted = true;
return trustchain;
},
})
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Steps>(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:
Expand All @@ -70,7 +58,17 @@ const StepFlow = ({
</>
);
case Steps.QrCodeMethod:
return <QrCodeMethod />;
return <QrCodeMethod currentOption={currentOption} setSelectedOption={setCurrentOption} />;

case Steps.PinDisplay:
return qrProcess.pinCode ? <PinCodeDisplay pinCode={qrProcess.pinCode} /> : null;

case Steps.PinInput:
return <PinCodeInput />;

case Steps.SyncError:
return <SyncError tryAgain={navigateToQrCodeMethod} />;

default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<typeof useAddAccountViewModel> & AddAccountProps;

Expand All @@ -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<Steps>(StartingStep);

const CustomDrawerHeader = () => <DrawerHeader onClose={onCloseAddAccountDrawer} />;

const handleStepChange = (step: Steps) => setCurrentStep(step);

let goBackCallback: () => void;

return (
<QueuedDrawer
isRequestingToBeOpened={isAddAccountDrawerVisible}
onClose={onCloseAddAccountDrawer}
CustomHeader={currentStep === Steps.QrCodeMethod ? CustomDrawerHeader : undefined}
hasBackButton={currentStep === Steps.ChooseSyncMethod}
onBack={() => goBackCallback()}
onBack={onGoBack}
>
<Flex maxHeight={"90%"}>
<StepFlow
startingStep={StartingStep}
doesNotHaveAccount={doesNotHaveAccount}
currency={currency}
onStepChange={handleStepChange}
onGoBack={callback => (goBackCallback = callback)}
currentStep={currentStep}
setCurrentStep={setCurrentStep}
setCurrentOption={setCurrentOption}
currentOption={currentOption}
navigateToChooseSyncMethod={navigateToChooseSyncMethod}
navigateToQrCodeMethod={navigateToQrCodeMethod}
qrProcess={qrProcess}
/>
</Flex>
</QueuedDrawer>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Steps>(startingStep);
const [currentOption, setCurrentOption] = useState<Options>(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,
Expand All @@ -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,
},
};
};

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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(<WalletSyncSettingsNavigator />, {
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();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -41,3 +42,20 @@ export function WalletSyncSharedNavigator() {
</QueryClientProvider>
);
}

export const INITIAL_TEST = (state: State) => ({
...state,
settings: {
...state.settings,
readOnlyModeEnabled: false,
overriddenFeatureFlags: {
llmWalletSync: {
enabled: true,
params: {
environment: "STAGING",
watchConfig: {},
},
},
},
},
});
Original file line number Diff line number Diff line change
@@ -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(<WalletSyncSettingsNavigator />, {
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);
});
});
Loading

0 comments on commit e1bdf18

Please sign in to comment.