diff --git a/.changeset/good-students-drop.md b/.changeset/good-students-drop.md
new file mode 100644
index 000000000000..a17cb4a26e1e
--- /dev/null
+++ b/.changeset/good-students-drop.md
@@ -0,0 +1,6 @@
+---
+"live-mobile": patch
+"@ledgerhq/native-ui": patch
+---
+
+Add the show qr code implementation for WS flow. Create tabSelector in RN UI Lib
diff --git a/apps/ledger-live-mobile/.unimportedrc.json b/apps/ledger-live-mobile/.unimportedrc.json
index 96637af194d8..c03bf0fe17cf 100644
--- a/apps/ledger-live-mobile/.unimportedrc.json
+++ b/apps/ledger-live-mobile/.unimportedrc.json
@@ -17,7 +17,9 @@
"src/contentCards/cards/vertical/*",
"src/**/__integrations__/*.tsx",
"src/MobileStorageProvider.ts",
- "src/newArch/components/Dummy/*.tsx"
+ "src/newArch/features/WalletSync/components/Error/index.tsx",
+ "src/newArch/features/WalletSync/screens/Synchronize/PinCodeDisplay.tsx",
+ "src/newArch/features/WalletSync/screens/Synchronize/PinCodeInput.tsx"
],
"ignoreUnused": [
"@react-native-masked-view/masked-view",
diff --git a/apps/ledger-live-mobile/jest.config.js b/apps/ledger-live-mobile/jest.config.js
index 18281ec804c1..afa2c7e78d6c 100644
--- a/apps/ledger-live-mobile/jest.config.js
+++ b/apps/ledger-live-mobile/jest.config.js
@@ -18,6 +18,7 @@ const transformIncludePatterns = [
"react-native-ble-plx",
"react-native-android-location-services-dialog-box",
"react-native-vector-icons",
+ "react-native-qrcode-svg",
];
/** @type {import('ts-jest').JestConfigWithTsJest} */
diff --git a/apps/ledger-live-mobile/src/images/bigSquareLogo.png b/apps/ledger-live-mobile/src/images/bigSquareLogo.png
new file mode 100644
index 000000000000..69b95e8872a0
Binary files /dev/null and b/apps/ledger-live-mobile/src/images/bigSquareLogo.png differ
diff --git a/apps/ledger-live-mobile/src/locales/en/common.json b/apps/ledger-live-mobile/src/locales/en/common.json
index ec5eefb43c03..cc23f5f0917d 100644
--- a/apps/ledger-live-mobile/src/locales/en/common.json
+++ b/apps/ledger-live-mobile/src/locales/en/common.json
@@ -6792,6 +6792,31 @@
"connectDevice": {
"title": "Use your Ledger"
}
+ },
+ "qrCode": {
+ "show": {
+ "title": "Show QR",
+ "explanation": {
+ "title": "Scan and synchronize your accounts using another Ledger Live app",
+ "steps": {
+ "step1": "Open the Ledger Live app you want to sync",
+ "step2": "Go to <0>Settings0> <1>>1> <0>General0> <1>>1> <0>Ledger Sync0> <1>>1> <0>Synchronize0>",
+ "step3": "Scan QR code until loader hits 100%."
+ }
+ }
+ },
+ "scan": {
+ "title": "Scan"
+ },
+ "pinCode": {
+ "title": "Enter your code",
+ "desc": "Type the code displayed on the Ledger Live you want to sync with.",
+ "error": {
+ "title": "Codes do not match",
+ "desc": "Make sure the code you type is the one displayed on the other Ledger Live instance.",
+ "tryAgain": "Try again"
+ }
+ }
}
}
},
diff --git a/apps/ledger-live-mobile/src/newArch/components/Dummy/Drawer.tsx b/apps/ledger-live-mobile/src/newArch/components/Dummy/Drawer.tsx
deleted file mode 100644
index 5809a0502d92..000000000000
--- a/apps/ledger-live-mobile/src/newArch/components/Dummy/Drawer.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from "react";
-import QueuedDrawer from "LLM/components/QueuedDrawer";
-import { Text } from "@ledgerhq/native-ui";
-
-type Props = {
- isOpen: boolean;
- handleClose: () => void;
-};
-
-const Drawer = ({ isOpen, handleClose }: Props) => {
- return (
-
- {"Dummy Drawer"}
-
- );
-};
-
-export default Drawer;
diff --git a/apps/ledger-live-mobile/src/newArch/components/QueuedDrawer/index.tsx b/apps/ledger-live-mobile/src/newArch/components/QueuedDrawer/index.tsx
index ea9c7a153cae..bc100759a8c3 100644
--- a/apps/ledger-live-mobile/src/newArch/components/QueuedDrawer/index.tsx
+++ b/apps/ledger-live-mobile/src/newArch/components/QueuedDrawer/index.tsx
@@ -63,6 +63,8 @@ const QueuedDrawer = ({
isRequestingToBeOpened = false,
isForcingToBeOpened = false,
onClose,
+ onBack,
+ hasBackButton,
onModalHide,
noCloseButton,
preventBackdropClick,
@@ -150,6 +152,8 @@ const QueuedDrawer = ({
preventBackdropClick={areDrawersLocked || preventBackdropClick}
onClose={handleCloseUserEvent}
onModalHide={handleModalHide}
+ onBack={onBack}
+ hasBackButton={hasBackButton}
noCloseButton={areDrawersLocked || noCloseButton}
modalStyle={style}
containerStyle={containerStyle}
diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/__integrations__/addAccount.integration.test.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/__integrations__/addAccount.integration.test.tsx
index 5a304672f2a5..eae148e6b200 100644
--- a/apps/ledger-live-mobile/src/newArch/features/Accounts/__integrations__/addAccount.integration.test.tsx
+++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/__integrations__/addAccount.integration.test.tsx
@@ -95,6 +95,7 @@ describe("AddAccount", () => {
await user.press(await screen.getByText(/import via another ledger live app/i));
});
await expect(await screen.findByText(/choose your sync method/i)).toBeVisible();
+ await expect(await screen.findByText(/Scan a QR code/i));
});
/**====== Import from desktop Test =======*/
diff --git a/apps/ledger-live-mobile/src/newArch/features/Accounts/__integrations__/shared.tsx b/apps/ledger-live-mobile/src/newArch/features/Accounts/__integrations__/shared.tsx
index 84154c2cd779..f1bb8dafc7a0 100644
--- a/apps/ledger-live-mobile/src/newArch/features/Accounts/__integrations__/shared.tsx
+++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/__integrations__/shared.tsx
@@ -14,7 +14,6 @@ const MockComponent = () => {
const openAddModal = () => setAddModalOpened(true);
const closeAddModal = () => setAddModalOpened(false);
- const reopenAddModal = () => setAddModalOpened(true);
return (
<>
@@ -30,11 +29,7 @@ const MockComponent = () => {
>
{t("portfolio.emptyState.buttons.import")}
-
+
>
);
};
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
new file mode 100644
index 000000000000..86e57a449f6b
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/screens/AddAccount/components/StepFlow.tsx
@@ -0,0 +1,82 @@
+import React, { useCallback, useEffect, useState } 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";
+
+type Props = {
+ startingStep: Steps;
+ currency?: CryptoCurrency | TokenCurrency | null;
+ doesNotHaveAccount?: boolean;
+ onStepChange?: (step: Steps) => void;
+ onGoBack?: (callback: () => void) => void;
+};
+
+const StepFlow = ({
+ startingStep,
+ doesNotHaveAccount,
+ currency,
+ onGoBack,
+ onStepChange,
+}: 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:
+ return (
+ <>
+
+
+ >
+ );
+ case Steps.ChooseSyncMethod:
+ return (
+ <>
+
+
+ >
+ );
+ case Steps.QrCodeMethod:
+ return ;
+ default:
+ return null;
+ }
+ };
+
+ return getScene();
+};
+
+export default StepFlow;
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 e3052d4e88e2..e4b61c4aec18 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,60 +1,55 @@
-import React from "react";
+import React, { useState } from "react";
import useAddAccountViewModel from "./useAddAccountViewModel";
import QueuedDrawer from "~/components/QueuedDrawer";
-import { TrackScreen } from "~/analytics";
-import SelectAddAccountMethod from "./components/SelectAddAccountMethod";
import { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
-import ChooseSyncMethod from "LLM/features/WalletSync/screens/Synchronize/ChooseMethod";
+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";
-type ViewProps = {
- isAddAccountDrawerVisible: boolean;
- doesNotHaveAccount?: boolean;
- currency?: CryptoCurrency | TokenCurrency | null;
- isWalletSyncDrawerVisible: boolean;
- onCloseAddAccountDrawer: () => void;
- reopenDrawer: () => void;
- onRequestToOpenWalletSyncDrawer: () => void;
- onCloseWalletSyncDrawer: () => void;
-};
+type ViewProps = ReturnType & AddAccountProps;
type AddAccountProps = {
isOpened: boolean;
currency?: CryptoCurrency | TokenCurrency | null;
doesNotHaveAccount?: boolean;
onClose: () => void;
- reopenDrawer: () => void;
};
+const StartingStep = Steps.AddAccountMethod;
+
function View({
isAddAccountDrawerVisible,
doesNotHaveAccount,
currency,
- isWalletSyncDrawerVisible,
onCloseAddAccountDrawer,
- onRequestToOpenWalletSyncDrawer,
- onCloseWalletSyncDrawer,
}: ViewProps) {
+ const [currentStep, setCurrentStep] = useState(StartingStep);
+
+ const CustomDrawerHeader = () => ;
+
+ const handleStepChange = (step: Steps) => setCurrentStep(step);
+
+ let goBackCallback: () => void;
+
return (
- <>
-
-
- goBackCallback()}
+ >
+
+ (goBackCallback = callback)}
/>
-
-
-
-
- >
+
+
);
}
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 c5702e01b3c8..b5c2dcad9417 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,17 +1,12 @@
-import { useCallback, useState } from "react";
+import { useCallback } from "react";
import { track } from "~/analytics";
-import { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
type AddAccountDrawerProps = {
isOpened: boolean;
- currency?: CryptoCurrency | TokenCurrency | null;
onClose: () => void;
- reopenDrawer: () => void;
};
-const useAddAccountViewModel = ({ isOpened, onClose, reopenDrawer }: AddAccountDrawerProps) => {
- const [isWalletSyncDrawerVisible, setWalletSyncDrawerVisible] = useState(false);
-
+const useAddAccountViewModel = ({ isOpened, onClose }: AddAccountDrawerProps) => {
const trackButtonClick = useCallback((button: string) => {
track("button_clicked", {
button,
@@ -24,22 +19,9 @@ const useAddAccountViewModel = ({ isOpened, onClose, reopenDrawer }: AddAccountD
onClose();
}, [trackButtonClick, onClose]);
- const onCloseWalletSyncDrawer = () => {
- setWalletSyncDrawerVisible(false);
- reopenDrawer();
- };
-
- const onRequestToOpenWalletSyncDrawer = () => {
- onCloseAddAccountDrawer();
- setWalletSyncDrawerVisible(true);
- };
-
return {
isAddAccountDrawerVisible: isOpened,
- isWalletSyncDrawerVisible,
onCloseAddAccountDrawer,
- onCloseWalletSyncDrawer,
- onRequestToOpenWalletSyncDrawer,
};
};
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
new file mode 100644
index 000000000000..45b2587c4af4
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/Accounts/types/enum/addAccount.ts
@@ -0,0 +1,5 @@
+export enum Steps {
+ AddAccountMethod = "AddAccountMethod",
+ ChooseSyncMethod = "ChooseSyncMethod",
+ QrCodeMethod = "QrCodeMethod",
+}
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 16b6d080ee28..4a6f8b27a710 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
@@ -5,40 +5,64 @@ import { WalletSyncSettingsNavigator } from "./shared";
import { State } from "~/reducers/types";
describe("WalletSyncSettings", () => {
- it("Should open wallet sync activation flow page from settings", async () => {
- const { user } = render(, {
- overrideInitialState: (state: State) => ({
- ...state,
- settings: {
- ...state.settings,
- readOnlyModeEnabled: false,
- overriddenFeatureFlags: {
- llmWalletSync: {
- enabled: true,
- params: {
- environment: "STAGING",
- watchConfig: {},
- },
- },
+ const initialState = (state: State) => ({
+ ...state,
+ settings: {
+ ...state.settings,
+ readOnlyModeEnabled: false,
+ overriddenFeatureFlags: {
+ llmWalletSync: {
+ enabled: true,
+ params: {
+ environment: "STAGING",
+ watchConfig: {},
},
},
- }),
- });
+ },
+ },
+ });
- // Check if the ledger sync row is visible
+ it("Should display the ledger sync row", async () => {
+ render(, { overrideInitialState: initialState });
await expect(await screen.findByText(/ledger sync/i)).toBeVisible();
+ });
- // On Press the ledger sync row
+ it("Should open the activation drawer when ledger sync row is pressed", async () => {
+ const { user } = render(, {
+ overrideInitialState: initialState,
+ });
await user.press(await screen.findByText(/ledger sync/i));
-
- // Check if the activation screen is visible
await expect(await screen.findByText(/sync your accounts across all platforms/i)).toBeVisible();
await expect(await screen.findByText(/already created a key?/i)).toBeVisible();
+ });
+
+ it("Should open the drawer when 'already created a key' button 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 expect(await screen.findByText(/choose your sync method/i)).toBeVisible();
+ });
- // On Press the already created a key link
+ 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();
+ });
- // Check if the drawer is visible
- await expect(await screen.findByText(/Choose your sync method/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/Actions.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Activation/Actions.tsx
index aaa2773b9122..d53968aad461 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Activation/Actions.tsx
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Activation/Actions.tsx
@@ -2,11 +2,11 @@ import React from "react";
import { Flex, Button, Link } from "@ledgerhq/native-ui";
import { useTranslation } from "react-i18next";
import {
- useWalletSyncAnalytics,
+ useLedgerSyncAnalytics,
AnalyticsButton,
AnalyticsPage,
AnalyticsFlow,
-} from "LLM/features/WalletSync/hooks/useWalletSyncAnalytics";
+} from "LLM/features/WalletSync/hooks/useLedgerSyncAnalytics";
type Props = {
onPressSyncAccounts: () => void;
@@ -15,13 +15,13 @@ type Props = {
const Actions = ({ onPressSyncAccounts, onPressHasAlreadyCreatedAKey }: Props) => {
const { t } = useTranslation();
- const { onClickTrack } = useWalletSyncAnalytics();
+ const { onClickTrack } = useLedgerSyncAnalytics();
const onPressSync = () => {
onClickTrack({
button: AnalyticsButton.SyncYourAccounts,
- page: AnalyticsPage.ActivateWalletSync,
- flow: AnalyticsFlow.WalletSync,
+ page: AnalyticsPage.ActivateLedgerSync,
+ flow: AnalyticsFlow.LedgerSync,
});
onPressSyncAccounts();
};
@@ -29,8 +29,8 @@ const Actions = ({ onPressSyncAccounts, onPressHasAlreadyCreatedAKey }: Props) =
const onPressHasAlreadyAKey = () => {
onClickTrack({
button: AnalyticsButton.AlreadyCreatedKey,
- page: AnalyticsPage.ActivateWalletSync,
- flow: AnalyticsFlow.WalletSync,
+ page: AnalyticsPage.ActivateLedgerSync,
+ flow: AnalyticsFlow.LedgerSync,
});
onPressHasAlreadyCreatedAKey();
};
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
new file mode 100644
index 000000000000..efa95edf3f4d
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Activation/ActivationFlow.tsx
@@ -0,0 +1,46 @@
+import React from "react";
+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 { AnalyticsPage } from "../../hooks/useLedgerSyncAnalytics";
+
+type Props = {
+ currentStep: Steps;
+ navigateToChooseSyncMethod: () => void;
+ navigateToQrCodeMethod: () => void;
+};
+
+const ActivationFlow = ({
+ currentStep,
+ navigateToChooseSyncMethod,
+ navigateToQrCodeMethod,
+}: Props) => {
+ const getScene = () => {
+ switch (currentStep) {
+ case Steps.Activation:
+ return (
+ <>
+
+
+ >
+ );
+ case Steps.ChooseSyncMethod:
+ return (
+ <>
+
+
+ >
+ );
+ case Steps.QrCodeMethod:
+ return ;
+ default:
+ return null;
+ }
+ };
+
+ return getScene();
+};
+
+export default ActivationFlow;
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Activation/index.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Activation/index.tsx
index 626acbb014ba..11b1d8641329 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Activation/index.tsx
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Activation/index.tsx
@@ -4,36 +4,22 @@ import IconsHeader from "./IconsHeader";
import { Flex, Text } from "@ledgerhq/native-ui";
import { useTranslation } from "react-i18next";
import { useTheme } from "styled-components/native";
-import { TrackScreen } from "~/analytics";
import { useInitMemberCredentials } from "../../hooks/useInitMemberCredentials";
-import QueuedDrawer from "~/components/QueuedDrawer";
-import ChooseSyncMethod from "../../screens/Synchronize/ChooseMethod";
-type Props = T extends true
- ? { isInsideDrawer: T; openSyncMethodDrawer: () => void }
- : { isInsideDrawer?: T; openSyncMethodDrawer?: undefined };
+type Props = { onSyncMethodPress: () => void };
-const Activation: React.FC> = ({ isInsideDrawer, openSyncMethodDrawer }) => {
+const Activation: React.FC = ({ onSyncMethodPress }) => {
const { colors } = useTheme();
const { t } = useTranslation();
- useInitMemberCredentials();
- const [isChooseMethodDrawerOpen, setIsChooseMethodDrawerOpen] = React.useState(false);
- const onPressSyncAccounts = () => {
- isInsideDrawer ? openSyncMethodDrawer() : setIsChooseMethodDrawerOpen(true);
- };
+ useInitMemberCredentials();
- const onPressHasAlreadyCreatedAKey = () => {
- isInsideDrawer ? openSyncMethodDrawer() : setIsChooseMethodDrawerOpen(true);
- };
+ const onPressSyncAccounts = () => onSyncMethodPress();
- const onPressCloseDrawer = () => {
- setIsChooseMethodDrawerOpen(false);
- };
+ const onPressHasAlreadyCreatedAKey = () => onSyncMethodPress();
return (
-
@@ -54,14 +40,6 @@ const Activation: React.FC> = ({ isInsideDrawer, openSyncMethodDr
onPressHasAlreadyCreatedAKey={onPressHasAlreadyCreatedAKey}
onPressSyncAccounts={onPressSyncAccounts}
/>
- {!isInsideDrawer && (
-
-
-
- )}
);
};
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Error/index.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Error/index.tsx
new file mode 100644
index 000000000000..57d6e188870c
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Error/index.tsx
@@ -0,0 +1,45 @@
+import { Box, Button, Flex, Icons, Text } from "@ledgerhq/native-ui";
+import React from "react";
+import styled, { useTheme } from "styled-components/native";
+type Props = {
+ title: string;
+ desc: string;
+ mainButton: {
+ label: string;
+ onPress: () => void;
+ };
+};
+
+export function ErrorComponent({ title, desc, mainButton }: Props) {
+ const { colors } = useTheme();
+ return (
+
+
+
+
+
+
+ {title}
+
+
+ {desc}
+
+
+
+
+
+
+ );
+}
+
+const Container = styled(Box)`
+ background-color: ${p => p.theme.colors.opacityDefault.c05};
+
+ height: 72px;
+ width: 72px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+`;
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/ManageInstances/DeletionError.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/ManageInstances/DeletionError.tsx
index cbba027bd789..3c8b47f18198 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/ManageInstances/DeletionError.tsx
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/ManageInstances/DeletionError.tsx
@@ -2,10 +2,10 @@ import React from "react";
import { Box, Icons, Flex, Text, Button, Link } from "@ledgerhq/native-ui";
import { useTranslation } from "react-i18next";
import {
- useWalletSyncAnalytics,
+ useLedgerSyncAnalytics,
AnalyticsButton,
AnalyticsPage,
-} from "../../hooks/useWalletSyncAnalytics";
+} from "../../hooks/useLedgerSyncAnalytics";
import styled, { useTheme } from "styled-components/native";
import TrackScreen from "~/analytics/TrackScreen";
@@ -32,7 +32,7 @@ type Props = {
};
export const DeletionError = ({ error, tryAgain, goToDelete, understood }: Props) => {
- const { onClickTrack } = useWalletSyncAnalytics();
+ const { onClickTrack } = useLedgerSyncAnalytics();
const onTryAgain = () => {
tryAgain?.();
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Synchronize/DrawerHeader.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Synchronize/DrawerHeader.tsx
new file mode 100644
index 000000000000..07b57e5b264d
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Synchronize/DrawerHeader.tsx
@@ -0,0 +1,46 @@
+import React from "react";
+import { Flex, Icons, Text } from "@ledgerhq/native-ui";
+import { useTheme } from "styled-components/native";
+import { useTranslation } from "react-i18next";
+import { TouchableOpacity } from "react-native";
+
+type Props = {
+ onClose: () => void;
+};
+
+const DrawerHeader: React.FC = ({ onClose }) => {
+ const { colors } = useTheme();
+ const { t } = useTranslation();
+
+ return (
+
+
+
+ {t("walletSync.walletSyncActivated.synchronize.title")}
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default DrawerHeader;
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
new file mode 100644
index 000000000000..a35fc9036c86
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/Synchronize/QrCode.tsx
@@ -0,0 +1,106 @@
+import React from "react";
+import { Flex, Text, NumberedList, ScrollContainer } from "@ledgerhq/native-ui";
+import styled, { useTheme } from "styled-components/native";
+import QRCode from "react-native-qrcode-svg";
+import getWindowDimensions from "~/logic/getWindowDimensions";
+import { Trans, useTranslation } from "react-i18next";
+
+const Italic = styled(Text)`
+ font-style: italic;
+`;
+// Won't work since we don't have inter italic font
+
+type Props = {
+ qrCodeValue: string;
+};
+
+const QrCode = ({ qrCodeValue }: Props) => {
+ const { colors } = useTheme();
+ const { width } = getWindowDimensions();
+ const { t } = useTranslation();
+
+ const backgroundBorderRadius = 23;
+ const backgroundPadding = 15.36;
+ const backgroundSize = 280;
+ const distanceBetweenQRCodeAndScreenBorder = 48;
+
+ const QRSize = Math.round(width - distanceBetweenQRCodeAndScreenBorder);
+ const maxQRCodeSize = backgroundSize - backgroundPadding * 2;
+ const QRCodeSize = Math.min(QRSize - backgroundPadding, maxQRCodeSize);
+
+ const steps = [
+ {
+ description: (
+
+ {t("walletSync.synchronize.qrCode.show.explanation.steps.step1")}
+
+ ),
+ },
+ {
+ description: (
+
+ ,
+ ,
+ ]}
+ />
+
+ ),
+ },
+ {
+ description: (
+
+ {t("walletSync.synchronize.qrCode.show.explanation.steps.step3")}
+
+ ),
+ },
+ ];
+
+ return (
+
+
+
+
+
+
+ {t("walletSync.synchronize.qrCode.show.explanation.title")}
+
+
+
+
+ );
+};
+
+export default QrCode;
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useWalletSyncAnalytics.ts b/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useLedgerSyncAnalytics.ts
similarity index 85%
rename from apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useWalletSyncAnalytics.ts
rename to apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useLedgerSyncAnalytics.ts
index 61a1f719a0ca..5f84a9deeea8 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useWalletSyncAnalytics.ts
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useLedgerSyncAnalytics.ts
@@ -1,10 +1,11 @@
import { track } from "~/analytics";
export enum AnalyticsPage {
- ActivateWalletSync = "Activate Wallet Sync",
+ ActivateLedgerSync = "Activate Ledger Sync",
ChooseSyncMethod = "Choose sync method",
BackupCreationSuccess = "Backup creation success",
ScanQRCode = "Scan QR code",
+ ShowQRCode = "Show QR code",
SyncWithQrCode = "Sync with QR code",
PinCode = "Pin code",
PinCodesDoNotMatch = "Pin codes don't match",
@@ -15,12 +16,12 @@ export enum AnalyticsPage {
ManageBackup = "Manage backup",
ConfirmDeleteBackup = "Confirm delete backup",
SyncWithNoKey = "Sync with no key",
- WalletSyncActivated = "Wallet Sync activated",
+ LedgerSyncActivated = "Ledger Sync activated",
AutoRemove = "Remove current instance",
}
export enum AnalyticsFlow {
- WalletSync = "Wallet Sync",
+ LedgerSync = "Ledger Sync",
}
export enum AnalyticsButton {
@@ -52,8 +53,8 @@ type OnClickTrack = {
flow?: (typeof AnalyticsFlow)[keyof typeof AnalyticsFlow];
};
-export function useWalletSyncAnalytics() {
- const onClickTrack = ({ button, page, flow }: OnClickTrack) => {
+export function useLedgerSyncAnalytics() {
+ const onClickTrack = ({ button, page, flow = AnalyticsFlow.LedgerSync }: OnClickTrack) => {
track("button_clicked", { button, page, flow });
};
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 b779777e00eb..f65830b54c74 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
@@ -1,40 +1,60 @@
import React from "react";
import QueuedDrawer from "LLM/components/QueuedDrawer";
-import Activation from "../../components/Activation";
import { TrackScreen } from "~/analytics";
-import ChooseSyncMethod from "../Synchronize/ChooseMethod";
+import { useWindowDimensions } from "react-native";
+import { Flex } from "@ledgerhq/native-ui";
+import ActivationFlow from "../../components/Activation/ActivationFlow";
+import { Steps } from "../../types/Activation";
+import DrawerHeader from "../../components/Synchronize/DrawerHeader";
+import useActivationDrawerModel from "./useActivationDrawerModel";
+
+type ViewProps = ReturnType;
type Props = {
isOpen: boolean;
- reopenDrawer: () => void;
+ startingStep: Steps;
handleClose: () => void;
};
-const ActivationDrawer = ({ isOpen, handleClose, reopenDrawer }: Props) => {
- const [isSyncMethodDrawerOpen, setIsSyncMethodDrawerOpen] = React.useState(false);
-
- const onPressCloseDrawer = () => {
- setIsSyncMethodDrawerOpen(false);
- reopenDrawer();
- };
-
- const openSyncMethodDrawer = () => {
- setIsSyncMethodDrawerOpen(true);
- handleClose();
- };
+function View({
+ isOpen,
+ currentStep,
+ hasCustomHeader,
+ canGoBack,
+ navigateToChooseSyncMethod,
+ navigateToQrCodeMethod,
+ goBackToPreviousStep,
+ handleClose,
+ onCloseDrawer,
+}: ViewProps) {
+ const { height } = useWindowDimensions();
+ const maxDrawerHeight = height - 180;
+ const CustomDrawerHeader = () => ;
return (
<>
-
-
-
-
-
-
+
+
+
+
>
);
+}
+
+const ActivationDrawer = (props: Props) => {
+ return ;
};
export default ActivationDrawer;
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/index.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/index.tsx
index 9ba0dc1ce683..76fcd55b4933 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/index.tsx
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/index.tsx
@@ -1,8 +1,15 @@
import React from "react";
import SafeAreaView from "~/components/SafeAreaView";
import Activation from "../../components/Activation";
+import ActivationDrawer from "./ActivationDrawer";
+import { Steps } from "../../types/Activation";
function View() {
+ const [showDrawer, setShowDrawer] = React.useState(false);
+
+ const onOpenDrawer = () => setShowDrawer(true);
+ const onCloseDrawer = () => setShowDrawer(false);
+
return (
-
+
+
+
);
}
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
new file mode 100644
index 000000000000..408856bf9941
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Activation/useActivationDrawerModel.ts
@@ -0,0 +1,67 @@
+import { useCallback, useState } from "react";
+import { Steps } from "../../types/Activation";
+import {
+ AnalyticsButton,
+ AnalyticsPage,
+ useLedgerSyncAnalytics,
+} from "../../hooks/useLedgerSyncAnalytics";
+
+type Props = {
+ isOpen: boolean;
+ startingStep: Steps;
+ handleClose: () => void;
+};
+
+const useActivationDrawerModel = ({ isOpen, startingStep, handleClose }: Props) => {
+ const { onClickTrack } = useLedgerSyncAnalytics();
+ const [currentStep, setCurrentStep] = useState(startingStep);
+
+ const hasCustomHeader = currentStep === Steps.QrCodeMethod;
+ const canGoBack = currentStep === Steps.ChooseSyncMethod && startingStep === Steps.Activation;
+
+ const getPreviousStep = useCallback(
+ (step: Steps): Steps => {
+ switch (step) {
+ case Steps.ChooseSyncMethod:
+ return Steps.Activation;
+ case Steps.QrCodeMethod:
+ return Steps.ChooseSyncMethod;
+ default:
+ return startingStep;
+ }
+ },
+ [startingStep],
+ );
+
+ const navigateToChooseSyncMethod = () => setCurrentStep(Steps.ChooseSyncMethod);
+
+ const navigateToQrCodeMethod = () => {
+ onClickTrack({
+ button: AnalyticsButton.ScanQRCode,
+ page: AnalyticsPage.ChooseSyncMethod,
+ });
+ setCurrentStep(Steps.QrCodeMethod);
+ };
+
+ const resetStep = () => setCurrentStep(startingStep);
+ const goBackToPreviousStep = () => setCurrentStep(getPreviousStep(currentStep));
+
+ const onCloseDrawer = () => {
+ resetStep();
+ handleClose();
+ };
+
+ return {
+ isOpen,
+ currentStep,
+ hasCustomHeader,
+ canGoBack,
+ navigateToChooseSyncMethod,
+ navigateToQrCodeMethod,
+ onCloseDrawer,
+ handleClose,
+ goBackToPreviousStep,
+ };
+};
+
+export default useActivationDrawerModel;
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Manage/index.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Manage/index.tsx
index 5ab335723745..e41f5a953dbf 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Manage/index.tsx
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Manage/index.tsx
@@ -1,13 +1,13 @@
import { Box, Flex, Text, Icons, InfiniteLoader, Alert } from "@ledgerhq/native-ui";
-import React, { useCallback } from "react";
+import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Option, OptionProps } from "./Option";
import styled from "styled-components";
import {
AnalyticsButton,
AnalyticsPage,
- useWalletSyncAnalytics,
-} from "../../hooks/useWalletSyncAnalytics";
+ useLedgerSyncAnalytics,
+} from "../../hooks/useLedgerSyncAnalytics";
import { Separator } from "../../components/Separator";
import { TouchableOpacity } from "react-native";
import { TrustchainNotFound } from "../../hooks/useGetMembers";
@@ -15,6 +15,8 @@ import ManageKeyDrawer from "../ManageKey/ManageKeyDrawer";
import { useManageKeyDrawer } from "../ManageKey/useManageKeyDrawer";
import ManageInstanceDrawer from "../ManageInstances/ManageInstancesDrawer";
import { useManageInstancesDrawer } from "../ManageInstances/useManageInstanceDrawer";
+import ActivationDrawer from "../Activation/ActivationDrawer";
+import { Steps } from "../../types/Activation";
const WalletSyncManage = () => {
const { t } = useTranslation();
@@ -24,24 +26,27 @@ const WalletSyncManage = () => {
const { data, isLoading, isError, error } = manageInstancesHook.memberHook;
- const { onClickTrack } = useWalletSyncAnalytics();
+ const { onClickTrack } = useLedgerSyncAnalytics();
- const goToManageBackup = useCallback(() => {
- manageKeyHook.openDrawer();
- onClickTrack({ button: AnalyticsButton.ManageKey, page: AnalyticsPage.WalletSyncActivated });
- }, [manageKeyHook, onClickTrack]);
+ const [isSyncDrawerOpen, setIsSyncDrawerOpen] = useState(false);
const goToSync = () => {
- //dispatch(setFlow({ flow: Flow.Synchronize, step: Step.SynchronizeMode }));
+ setIsSyncDrawerOpen(true);
+ onClickTrack({ button: AnalyticsButton.Synchronize, page: AnalyticsPage.LedgerSyncActivated });
+ };
- onClickTrack({ button: AnalyticsButton.Synchronize, page: AnalyticsPage.WalletSyncActivated });
+ const closeSyncDrawer = () => setIsSyncDrawerOpen(false);
+
+ const goToManageBackup = () => {
+ manageKeyHook.openDrawer();
+ onClickTrack({ button: AnalyticsButton.ManageKey, page: AnalyticsPage.LedgerSyncActivated });
};
const goToManageInstances = () => {
manageInstancesHook.openDrawer();
onClickTrack({
button: AnalyticsButton.ManageSynchronizations,
- page: AnalyticsPage.WalletSyncActivated,
+ page: AnalyticsPage.LedgerSyncActivated,
});
};
@@ -117,9 +122,11 @@ const WalletSyncManage = () => {
- {/**
- * DRAWERS
- */}
+
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/ChooseMethod.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/ChooseMethod.tsx
index 94651bbbd808..0c91472036f9 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/ChooseMethod.tsx
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/ChooseMethod.tsx
@@ -6,21 +6,31 @@ import { useNavigation } from "@react-navigation/native";
import { NavigatorName, ScreenName } from "~/const";
import { BaseComposite, StackNavigatorProps } from "~/components/RootNavigator/types/helpers";
import { WalletSyncNavigatorStackParamList } from "~/components/RootNavigator/types/WalletSyncNavigator";
+import {
+ useLedgerSyncAnalytics,
+ AnalyticsButton,
+ AnalyticsPage,
+} from "../../hooks/useLedgerSyncAnalytics";
type NavigationProps = BaseComposite<
StackNavigatorProps
>;
-const ChooseSyncMethod = () => {
+type Props = {
+ onScanMethodPress: () => void;
+};
+
+const ChooseSyncMethod = ({ onScanMethodPress }: Props) => {
const { t } = useTranslation();
const navigation = useNavigation();
+ const { onClickTrack } = useLedgerSyncAnalytics();
const onConnectDeviceMethodPress = () => {
+ onClickTrack({ button: AnalyticsButton.UseYourLedger, page: AnalyticsPage.ChooseSyncMethod });
navigation.navigate(NavigatorName.WalletSync, {
screen: ScreenName.WalletSyncActivationProcess,
});
};
- const onScanMethodPress = () => {};
return (
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/PinCodeDisplay.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/PinCodeDisplay.tsx
new file mode 100644
index 000000000000..5cf1bb0f86f7
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/PinCodeDisplay.tsx
@@ -0,0 +1,49 @@
+import React from "react";
+import { useTranslation } from "react-i18next";
+import { Flex, Text } from "@ledgerhq/native-ui";
+import styled, { useTheme } from "styled-components/native";
+import { AnalyticsPage } from "../../hooks/useLedgerSyncAnalytics";
+import TrackScreen from "~/analytics/TrackScreen";
+
+type Props = {
+ pinCode: string;
+};
+
+export default function PinCodeDisplay({ pinCode }: Props) {
+ const { t } = useTranslation();
+ const { colors } = useTheme();
+
+ return (
+
+
+
+ {t("walletSync.synchronize.qrCode.pinCode.title")}
+
+
+
+ {t("walletSync.synchronize.qrCode.pinCode.desc")}
+
+
+
+ {pinCode?.split("").map((digit, index) => (
+
+
+ {digit}
+
+
+ ))}
+
+
+ );
+}
+
+const NumberContainer = styled(Flex)`
+ border-radius: 8px;
+ height: 50px;
+ width: 50px;
+`;
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/PinCodeInput.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/PinCodeInput.tsx
new file mode 100644
index 000000000000..7f5ad7f9facc
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/PinCodeInput.tsx
@@ -0,0 +1,109 @@
+import React, { forwardRef, useImperativeHandle, useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
+import { Flex, Text } from "@ledgerhq/native-ui";
+import styled from "styled-components/native";
+import { AnalyticsPage } from "../../hooks/useLedgerSyncAnalytics";
+import TrackScreen from "~/analytics/TrackScreen";
+import { NativeSyntheticEvent, TextInput, TextInputKeyPressEventData } from "react-native";
+
+export default function PinCodeInput() {
+ const { t } = useTranslation();
+ const inputRefs = [useRef(null), useRef(null), useRef(null)];
+ const [digits, setDigits] = useState(["", "", ""]);
+
+ const handleChange = (value: string, index: number) => {
+ const newDigits = [...digits];
+ newDigits[index] = value;
+ setDigits(newDigits);
+ if (value && index < digits.length - 1) {
+ inputRefs[index + 1].current?.focus();
+ }
+ };
+
+ const handleKeyPress = (e: NativeSyntheticEvent, index: number) => {
+ if (e.nativeEvent.key === "Backspace") {
+ if (!digits[index] && index > 0) {
+ inputRefs[index - 1].current?.focus();
+ } else {
+ const newDigits = [...digits];
+ newDigits[index] = "";
+ setDigits(newDigits);
+ }
+ }
+ };
+
+ return (
+
+
+
+ {t("walletSync.synchronize.qrCode.pinCode.title")}
+
+
+ {t("walletSync.synchronize.qrCode.pinCode.desc")}
+
+
+ {digits.map((digit, index) => (
+ handleChange(value, index)}
+ onKeyPress={e => handleKeyPress(e, index)}
+ index={index}
+ ref={inputRefs[index]}
+ />
+ ))}
+
+
+ );
+}
+
+interface DigitInputProps {
+ value: string;
+ onChange: (value: string) => void;
+ onKeyPress: (e: NativeSyntheticEvent, index: number) => void;
+ index: number;
+}
+
+interface TextInputRef {
+ focus: () => void;
+}
+
+const DigitInput = forwardRef(
+ ({ value, onChange, onKeyPress, index }, forwardedRef) => {
+ const [isFocused, setIsFocused] = useState(false);
+ const inputRef = useRef(null);
+
+ useImperativeHandle(forwardedRef, () => ({
+ focus: () => inputRef.current?.focus(),
+ }));
+
+ const handleChange = (text: string) => {
+ if (text.length <= 1 && /^\d*$/.test(text)) {
+ onChange(text);
+ }
+ };
+
+ return (
+ setIsFocused(true)}
+ onBlur={() => setIsFocused(false)}
+ isFocused={isFocused}
+ onKeyPress={e => onKeyPress(e, index)}
+ textAlign="center"
+ />
+ );
+ },
+);
+
+const NumberContainer = styled(TextInput)<{ isFocused: boolean }>`
+ border-radius: 8px;
+ height: 50px;
+ width: 50px;
+ border: 1px solid
+ ${({ theme, isFocused }) => (isFocused ? theme.colors.primary.c80 : theme.colors.neutral.c40)};
+`;
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
new file mode 100644
index 000000000000..40d64001a887
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Synchronize/QrCodeMethod.tsx
@@ -0,0 +1,70 @@
+import React, { useState } 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";
+import { useTranslation } from "react-i18next";
+import {
+ useLedgerSyncAnalytics,
+ AnalyticsPage,
+ AnalyticsButton,
+} from "../../hooks/useLedgerSyncAnalytics";
+import { TrackScreen } from "~/analytics";
+
+const QrCodeMethod = () => {
+ const [selectedOption, setSelectedOption] = useState(Options.SCAN);
+ const { onClickTrack } = useLedgerSyncAnalytics();
+ const { t } = useTranslation();
+
+ const handleSelectOption = (option: OptionsType) => {
+ setSelectedOption(option);
+ const button =
+ option === Options.SCAN ? AnalyticsButton.ScanQRCode : AnalyticsButton.ShowQRCode;
+ onClickTrack({
+ button,
+ page: AnalyticsPage.ScanQRCode,
+ });
+ };
+
+ const renderSwitch = () => {
+ switch (selectedOption) {
+ case Options.SCAN:
+ return (
+ <>
+
+
+ >
+ );
+ case Options.SHOW_QR:
+ return (
+ <>
+
+
+ >
+ );
+ }
+ };
+
+ return (
+
+
+ {renderSwitch()}
+
+ );
+};
+
+export default QrCodeMethod;
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
new file mode 100644
index 000000000000..8b0f6fb7368c
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/types/Activation.ts
@@ -0,0 +1,12 @@
+export enum Options {
+ SCAN = "scan",
+ SHOW_QR = "showQR",
+}
+
+export type OptionsType = Options.SCAN | Options.SHOW_QR;
+
+export enum Steps {
+ Activation = "Activation",
+ ChooseSyncMethod = "ChooseSyncMethod",
+ QrCodeMethod = "QrCodeMethod",
+}
diff --git a/apps/ledger-live-mobile/src/screens/Accounts/AddAccount.tsx b/apps/ledger-live-mobile/src/screens/Accounts/AddAccount.tsx
index a863673789e4..f088ef7105a6 100644
--- a/apps/ledger-live-mobile/src/screens/Accounts/AddAccount.tsx
+++ b/apps/ledger-live-mobile/src/screens/Accounts/AddAccount.tsx
@@ -23,10 +23,6 @@ function AddAccount({ currencyId }: { currencyId?: string }) {
setIsAddModalOpened(false);
}
- function reopenAddModal() {
- setIsAddModalOpened(true);
- }
-
return (
<>
@@ -42,12 +38,7 @@ function AddAccount({ currencyId }: { currencyId?: string }) {
-
+
>
);
}
diff --git a/apps/ledger-live-mobile/src/screens/Assets/index.tsx b/apps/ledger-live-mobile/src/screens/Assets/index.tsx
index 09e57f0f6cf1..04968e83f926 100644
--- a/apps/ledger-live-mobile/src/screens/Assets/index.tsx
+++ b/apps/ledger-live-mobile/src/screens/Assets/index.tsx
@@ -65,8 +65,6 @@ function Assets() {
const closeAddModal = useCallback(() => setAddModalOpened(false), [setAddModalOpened]);
- const reopenAddModal = useCallback(() => setAddModalOpened(true), [setAddModalOpened]);
-
const renderItem = useCallback(
({ item }: { item: Asset }) => (
@@ -118,11 +116,7 @@ function Assets() {
/>
-
+
);
diff --git a/apps/ledger-live-mobile/src/screens/Onboarding/steps/accessExistingWallet.tsx b/apps/ledger-live-mobile/src/screens/Onboarding/steps/accessExistingWallet.tsx
index da4e3cdab234..5aaf642fbc34 100644
--- a/apps/ledger-live-mobile/src/screens/Onboarding/steps/accessExistingWallet.tsx
+++ b/apps/ledger-live-mobile/src/screens/Onboarding/steps/accessExistingWallet.tsx
@@ -15,6 +15,7 @@ import OnboardingView from "./OnboardingView";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
import { logDrawer } from "LLM/components/QueuedDrawer/utils/logDrawer";
import ActivationDrawer from "LLM/features/WalletSync/screens/Activation/ActivationDrawer";
+import { Steps } from "LLM/features/WalletSync/types/Activation";
type NavigationProps = StackNavigatorProps<
OnboardingNavigatorParamList & BaseNavigatorStackParamList,
@@ -55,11 +56,6 @@ function AccessExistingWallet() {
logDrawer("Wallet Sync Welcome back", "close");
}, []);
- const reopenDrawer = useCallback(() => {
- setIsDrawerVisible(true);
- logDrawer("Wallet Sync Welcome back", "reopen");
- }, []);
-
return (
+
);
diff --git a/apps/ledger-live-mobile/src/screens/Portfolio/EmptyStatePortfolio.tsx b/apps/ledger-live-mobile/src/screens/Portfolio/EmptyStatePortfolio.tsx
index 4bc717d5506e..ebb9b0f3b80d 100644
--- a/apps/ledger-live-mobile/src/screens/Portfolio/EmptyStatePortfolio.tsx
+++ b/apps/ledger-live-mobile/src/screens/Portfolio/EmptyStatePortfolio.tsx
@@ -33,10 +33,6 @@ function EmptyStatePortfolio({ showHelp = true }: Props) {
const closeAddModal = useCallback(() => setAddModalOpened(false), [setAddModalOpened]);
- const reopenAddModal = useCallback(() => {
- setAddModalOpened(true);
- }, [setAddModalOpened]);
-
const navigateToManager = useCallback(() => {
navigation.navigate(NavigatorName.MyLedger);
}, [navigation]);
@@ -100,7 +96,7 @@ function EmptyStatePortfolio({ showHelp = true }: Props) {
diff --git a/apps/ledger-live-mobile/src/screens/Portfolio/index.tsx b/apps/ledger-live-mobile/src/screens/Portfolio/index.tsx
index aba3472b105f..38d72b130a02 100644
--- a/apps/ledger-live-mobile/src/screens/Portfolio/index.tsx
+++ b/apps/ledger-live-mobile/src/screens/Portfolio/index.tsx
@@ -127,7 +127,6 @@ function PortfolioScreen({ navigation }: NavigationProps) {
}, [setAddModalOpened]);
const closeAddModal = useCallback(() => setAddModalOpened(false), [setAddModalOpened]);
- const reopenAddModal = useCallback(() => setAddModalOpened(true), [setAddModalOpened]);
const refreshAccountsOrdering = useRefreshAccountsOrdering();
useFocusEffect(refreshAccountsOrdering);
@@ -227,7 +226,6 @@ function PortfolioScreen({ navigation }: NavigationProps) {
diff --git a/apps/ledger-live-mobile/src/screens/Settings/General/WalletSyncRow.tsx b/apps/ledger-live-mobile/src/screens/Settings/General/WalletSyncRow.tsx
index c78861279a63..d7ae699d017c 100644
--- a/apps/ledger-live-mobile/src/screens/Settings/General/WalletSyncRow.tsx
+++ b/apps/ledger-live-mobile/src/screens/Settings/General/WalletSyncRow.tsx
@@ -1,21 +1,27 @@
-import React, { useCallback } from "react";
+import React, { useCallback, useState } from "react";
import SettingsRow from "~/components/SettingsRow";
import { useTranslation } from "react-i18next";
import { useNavigation } from "@react-navigation/native";
import { NavigatorName, ScreenName } from "~/const";
import {
- useWalletSyncAnalytics,
+ useLedgerSyncAnalytics,
AnalyticsPage,
AnalyticsButton,
-} from "LLM/features/WalletSync/hooks/useWalletSyncAnalytics";
+} from "LLM/features/WalletSync/hooks/useLedgerSyncAnalytics";
import { useSelector } from "react-redux";
import { trustchainSelector } from "@ledgerhq/trustchain/store";
+import ActivationDrawer from "LLM/features/WalletSync/screens/Activation/ActivationDrawer";
+import { Steps } from "LLM/features/WalletSync/types/Activation";
const WalletSyncRow = () => {
const { t } = useTranslation();
- const { onClickTrack } = useWalletSyncAnalytics();
+ const { onClickTrack } = useLedgerSyncAnalytics();
const navigation = useNavigation();
+ const [isDrawerVisible, setIsDrawerVisible] = useState(false);
+ const closeDrawer = useCallback(() => {
+ setIsDrawerVisible(false);
+ }, []);
const trustchain = useSelector(trustchainSelector);
const navigateToWalletSyncActivationScreen = useCallback(() => {
@@ -27,21 +33,27 @@ const WalletSyncRow = () => {
screen: ScreenName.WalletSyncActivated,
});
} else {
- navigation.navigate(NavigatorName.WalletSync, {
- screen: ScreenName.WalletSyncActivationInit,
- });
+ setIsDrawerVisible(true);
}
}, [navigation, onClickTrack, trustchain?.rootId]);
return (
-
+ <>
+
+
+
+ >
);
};
diff --git a/libs/ui/packages/native/src/components/Form/TabSelector/index.tsx b/libs/ui/packages/native/src/components/Form/TabSelector/index.tsx
new file mode 100644
index 000000000000..701ad3b2712f
--- /dev/null
+++ b/libs/ui/packages/native/src/components/Form/TabSelector/index.tsx
@@ -0,0 +1,122 @@
+import React, { useEffect } from "react";
+import Text from "../../Text";
+import Flex from "../../Layout/Flex";
+import styled, { useTheme } from "styled-components/native";
+import { TouchableOpacity } from "react-native";
+import Animated, { useSharedValue, useAnimatedStyle, withSpring } from "react-native-reanimated";
+
+const StyledTouchableOpacity = styled(TouchableOpacity)`
+ flex: 1;
+ overflow: hidden;
+`;
+
+const StyledFlex = styled(Flex)<{ isSelected: boolean }>`
+ width: 100%;
+ height: 100%;
+ justify-content: center;
+ align-items: center;
+`;
+
+const StyledText = styled(Text)<{ isSelected: boolean }>`
+ line-height: 14.52px;
+ text-align: center;
+ font-size: 12px;
+ color: ${(p) =>
+ p.isSelected ? p.theme.colors.constant.black : p.theme.colors.opacityDefault.c50};
+`;
+
+interface OptionButtonProps {
+ option: T;
+ selectedOption: T;
+ handleSelectOption: (option: T) => void;
+ label: string;
+}
+
+const OptionButton = ({
+ option,
+ selectedOption,
+ handleSelectOption,
+ label,
+}: OptionButtonProps) => {
+ const isSelected = selectedOption === option;
+
+ return (
+ handleSelectOption(option)}>
+
+
+ {label}
+
+
+
+ );
+};
+
+interface TabSelectorProps {
+ options: T[];
+ selectedOption: T;
+ handleSelectOption: (option: T) => void;
+ labels: { [key in T]: string };
+}
+
+export default function TabSelector({
+ options,
+ selectedOption,
+ handleSelectOption,
+ labels,
+}: TabSelectorProps): JSX.Element {
+ const { colors } = useTheme();
+ const translateX = useSharedValue(-39);
+
+ useEffect(() => {
+ translateX.value = withSpring(selectedOption === options[0] ? -40 : 40, {
+ damping: 30,
+ stiffness: 80,
+ });
+ }, [selectedOption, translateX, options]);
+
+ const animatedStyle = useAnimatedStyle(() => {
+ return {
+ transform: [{ translateX: translateX.value }],
+ };
+ });
+
+ return (
+
+
+ {options.map((option) => (
+
+ ))}
+
+ );
+}
diff --git a/libs/ui/packages/native/src/components/Form/index.ts b/libs/ui/packages/native/src/components/Form/index.ts
index b90403476184..3f14bbd68d27 100644
--- a/libs/ui/packages/native/src/components/Form/index.ts
+++ b/libs/ui/packages/native/src/components/Form/index.ts
@@ -4,3 +4,4 @@ export { default as Slider } from "./Slider";
export { default as Switch } from "./Switch";
export { default as Toggle } from "./Toggle";
export { default as SelectableList } from "./SelectableList";
+export { default as TabSelector } from "./TabSelector";
diff --git a/libs/ui/packages/native/src/components/Layout/Modals/BaseModal/index.tsx b/libs/ui/packages/native/src/components/Layout/Modals/BaseModal/index.tsx
index e4014ca782f1..e83ba0d5ed25 100644
--- a/libs/ui/packages/native/src/components/Layout/Modals/BaseModal/index.tsx
+++ b/libs/ui/packages/native/src/components/Layout/Modals/BaseModal/index.tsx
@@ -1,7 +1,7 @@
import React, { ReactNode, useCallback } from "react";
import ReactNativeModal, { ModalProps } from "react-native-modal";
import styled from "styled-components/native";
-import { StyleProp, ViewStyle } from "react-native";
+import { StyleProp, TouchableOpacity, ViewStyle } from "react-native";
import sizes from "../../../../helpers/getDeviceSize";
import Text from "../../../Text";
@@ -9,7 +9,7 @@ import { IconOrElementType } from "../../../Icon/type";
import { BoxedIcon } from "../../../Icon";
import { Flex } from "../../index";
import { space } from "styled-system";
-import { Close } from "@ledgerhq/icons-ui/native";
+import { Close, ArrowLeft } from "@ledgerhq/icons-ui/native";
import { useTheme } from "styled-components/native";
const { width, height } = sizes;
@@ -17,6 +17,7 @@ const { width, height } = sizes;
export type BaseModalProps = {
isOpen?: boolean;
onClose?: () => void;
+ onBack?: () => void;
modalStyle?: StyleProp;
safeContainerStyle?: StyleProp;
containerStyle?: StyleProp;
@@ -28,6 +29,7 @@ export type BaseModalProps = {
subtitle?: string;
children?: React.ReactNode;
noCloseButton?: boolean;
+ hasBackButton?: boolean;
CustomHeader?: React.ComponentType<{ children?: ReactNode }>;
} & Partial;
@@ -54,6 +56,13 @@ const CloseContainer = styled.View`
z-index: 10;
`;
+const BackContainer = styled.View`
+ display: flex;
+ align-items: flex-start;
+ margin-bottom: ${(p) => p.theme.space[6]}px;
+ z-index: 10;
+`;
+
const ClosePressableExtendedBounds = styled.TouchableOpacity.attrs({
p: 3,
})`
@@ -127,10 +136,26 @@ export function ModalHeaderCloseButton({
);
}
+export function ModalHeaderBackButton({
+ onBack,
+}: Pick): React.ReactElement {
+ const { colors } = useTheme();
+
+ return (
+
+
+
+
+
+ );
+}
+
export default function BaseModal({
isOpen,
onClose = () => {},
+ onBack = () => {},
noCloseButton,
+ hasBackButton,
safeContainerStyle = {},
containerStyle = {},
modalStyle = {},
@@ -181,7 +206,23 @@ export default function BaseModal({
)}
- {!CustomHeader && !noCloseButton && }
+
+ {!CustomHeader && onBack && hasBackButton && (
+
+
+
+ )}
+ {!CustomHeader && !noCloseButton && (
+
+
+
+ )}
+
{
+ const [selectedOption, setSelectedOption] = useState(args.selectedOption);
+
+ return (
+
+
+
+ );
+};
+
+TabSelectorStory.storyName = "TabSelectorStory";
+
+const TabSelectorStoryArgs = {
+ options: ["option1", "option2"],
+ selectedOption: "option1",
+ labels: {
+ option1: "Option 1",
+ option2: "Option 2",
+ },
+};
+
+TabSelectorStory.args = TabSelectorStoryArgs;
diff --git a/libs/ui/packages/native/storybook/stories/index.ts b/libs/ui/packages/native/storybook/stories/index.ts
index d630613a862a..f5edc8322ffa 100644
--- a/libs/ui/packages/native/storybook/stories/index.ts
+++ b/libs/ui/packages/native/storybook/stories/index.ts
@@ -31,6 +31,7 @@ export * as NumberInputStory from "./Form/Input/NumberInput.stories";
export * as LintStory from "./Link/Link.stories";
export * as SwitchStory from "./Form/Switch.stories";
export * as ToggleStory from "./Form/Toggle.stories";
+export * as TabSelector from "./Form/TabSelector.stories";
export * as ChipTabsStory from "./Tabs/ChipTabs/ChipTabs.stories";
export * as GraphTabsStory from "./Tabs/GraphTabs/GraphTabs.stories";
export * as BadgeStory from "./Tag/Badge.stories";