Skip to content

Commit

Permalink
[LLD] - [LIVE-12264] - Add Wallet Sync activation screen (#6782)
Browse files Browse the repository at this point in the history
* Add Activation screen for Wallet Sync

* Add WalletSync store

* Split WalletSync by Activaton & Manage side content

* Add changeset

* Add first test

* fix lint

* Fix test with translation

---------

Co-authored-by: Martin Cayuelas <[email protected]>
  • Loading branch information
sshmaxime and mcayuelas-ledger committed May 20, 2024
1 parent f72f4d5 commit 879fda4
Show file tree
Hide file tree
Showing 16 changed files with 224 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .changeset/swift-peaches-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ledger-live-desktop": patch
---

Add Wallet Sync activation screen
3 changes: 2 additions & 1 deletion apps/ledger-live-desktop/.unimportedrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"**/types.js",
"**/types.*",
"src/generate-cryptoassets-md.ts",
"src/newArch/Collectibles/**"
"src/newArch/Collectibles/**",
"src/renderer/screens/settings/sections/General/WalletSync/setupTests/shared.tsx"
],
"ignoreUnused": ["@types/semver", "@types/qrcode", "@types/react-key-handler", "prop-types"],
"aliases": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ export type DrawerProps = {
style?: React.CSSProperties;
withPaddingTop?: boolean;
};
const domNode = document.getElementById("modals");

export function SideDrawer({
children,
Expand All @@ -116,6 +115,7 @@ export function SideDrawer({
forceDisableFocusTrap = false,
...props
}: DrawerProps) {
const domNode = document.getElementById("modals");
const deviceBlocked = useDeviceBlocked();

const onKeyPress = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,7 @@ describe("RecipientField", () => {
recipientDomain: undefined,
});
});
expect(
screen.getByText("send.steps.recipient.domainService.noResolution.title"),
).toBeTruthy();
expect(screen.getByText("No address found for this domain")).toBeTruthy();

expect(screen.getByTestId("domain-error-no-resolution")).toBeTruthy();
});
Expand Down
3 changes: 3 additions & 0 deletions apps/ledger-live-desktop/src/renderer/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import postOnboarding from "@ledgerhq/live-common/postOnboarding/reducer";
import market, { MarketState } from "./market";
import wallet from "./wallet";
import { WalletState } from "@ledgerhq/live-wallet/store";
import walletSync, { WalletSyncState } from "./walletSync";

export type State = {
accounts: AccountsState;
Expand All @@ -25,6 +26,7 @@ export type State = {
postOnboarding: PostOnboardingState;
market: MarketState;
wallet: WalletState;
walletSync: WalletSyncState;
};

export default combineReducers({
Expand All @@ -39,4 +41,5 @@ export default combineReducers({
swap,
market,
wallet,
walletSync,
});
36 changes: 36 additions & 0 deletions apps/ledger-live-desktop/src/renderer/reducers/walletSync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { handleActions } from "redux-actions";
import { Handlers } from "./types";

export type WalletSyncState = {
activated: boolean;
};

const initialState: WalletSyncState = {
activated: false,
};

type HandlersPayloads = {
WALLET_SYNC_ACTIVATE: boolean;
WALLET_SYNC_DEACTIVATE: boolean;
};

type MarketHandlers<PreciseKey = true> = Handlers<WalletSyncState, HandlersPayloads, PreciseKey>;

const handlers: MarketHandlers = {
WALLET_SYNC_ACTIVATE: (state: WalletSyncState) => ({
...state,
activated: true,
}),
WALLET_SYNC_DEACTIVATE: (state: WalletSyncState) => ({
...state,
activated: false,
}),
};

// Selectors
export const walletSyncSelector = (state: { walletSync: WalletSyncState }) => state.walletSync;

export default handleActions<WalletSyncState, HandlersPayloads[keyof HandlersPayloads]>(
handlers as unknown as MarketHandlers<false>,
initialState,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Box, Flex, Icons, Text } from "@ledgerhq/react-ui";
import React, { PropsWithChildren } from "react";
import { useTranslation } from "react-i18next";
import ButtonV3 from "~/renderer/components/ButtonV3";
import useTheme from "~/renderer/hooks/useTheme";

const LogoWrapper = ({ children, opacity = "70%" }: PropsWithChildren & { opacity?: string }) => (
<Box>
<Flex padding="7px" borderRadius="13px" border="1px solid hsla(0, 0%, 100%, 0.05)">
<Flex
borderRadius="9px"
backgroundColor="hsla(248, 100%, 85%, 0.08)"
padding="5px"
opacity={opacity}
>
{children}
</Flex>
</Flex>
</Box>
);

const WalletSyncActivation = () => {
const { colors } = useTheme();
const { t } = useTranslation();

return (
<Flex
flexDirection="column"
height="100%"
paddingX="64px"
alignSelf="center"
justifyContent="center"
rowGap="48px"
>
<Flex flexDirection="column" alignSelf="center" justifyContent="center" rowGap="24px">
<Flex justifyContent="center" alignItems="center">
<LogoWrapper>
<Icons.Mobile color={colors.constant.purple} />
</LogoWrapper>

<LogoWrapper opacity="100%">
<Icons.Refresh size="L" color={colors.constant.purple} />
</LogoWrapper>

<LogoWrapper>
<Icons.Desktop color={colors.constant.purple} />
</LogoWrapper>
</Flex>

<Text fontSize={24} variant="h4Inter" textAlign="center">
{t("walletSync.activate.title")}
</Text>
<Text fontSize={14} variant="body" color="hsla(0, 0%, 58%, 1)" textAlign="center">
{t("walletSync.activate.description")}
</Text>
<Flex justifyContent="center">
<ButtonV3 variant="main"> {t("walletSync.activate.cta")}</ButtonV3>
</Flex>
</Flex>

<Box>
<Flex
flexDirection="row"
padding="18px"
borderRadius="12px"
backgroundColor={colors.opacityDefault.c05}
justifyContent="space-between"
>
<Text variant="body" fontSize={14} flexShrink={1}>
{t("walletSync.alreadySync.title")}
</Text>
<Box>
<ButtonV3 variant="shade">{t("walletSync.alreadySync.cta")}</ButtonV3>
</Box>
</Flex>
</Box>
</Flex>
);
};

export default WalletSyncActivation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from "react";

const WalletSyncManage = () => {
return <div>Wallet Sync</div>;
};

export default WalletSyncManage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* @jest-environment jsdom
*/

import { describe, it, expect } from "@jest/globals";

import React from "react";
import { render, screen, waitFor } from "tests/testUtils";
import "@testing-library/jest-dom";
import { WalletSyncPages } from "../setupTests/shared";

describe("Rendering", () => {
it("should loads and displays WalletSync Row", async () => {
render(<WalletSyncPages />);
expect(screen.getByRole("button", { name: "Manage" })).toBeTruthy();
});

it("should open drawer and display Wallet Sync Activation flow", async () => {
const { user } = render(<WalletSyncPages />);
const button = screen.getByRole("button", { name: "Manage" });

await user.click(button);
await waitFor(() =>
expect(screen.getByRole("button", { name: "Create a backup" })).toBeDefined(),
);

expect(screen.getByRole("button", { name: "Synchronize" })).toBeDefined();
});
});
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
import React, { useState } from "react";
import { useSelector } from "react-redux";
import Button from "~/renderer/components/Button";
import { SideDrawer } from "~/renderer/components/SideDrawer";

const SideContentActivateWalletSync = () => {
return <div>Activate Wallet Sync</div>;
};

const SideContentWalletSync = () => {
return <div>Wallet Sync</div>;
};
import { walletSyncSelector } from "~/renderer/reducers/walletSync";
import WalletSyncActivation from "./SideContent/Activation";
import WalletSyncManage from "./SideContent/Manage";
import { useTranslation } from "react-i18next";

const WalletSyncRow = () => {
const lldWalletSync = useFeature("lldWalletSync");

const walletSync = useSelector(walletSyncSelector);
const [open, setOpen] = useState(false);
const { t } = useTranslation();

return (
<>
<SideDrawer isOpen={open} onRequestClose={() => setOpen(false)} direction="left">
{lldWalletSync?.enabled ? <SideContentWalletSync /> : <SideContentActivateWalletSync />}
{walletSync.activated ? <WalletSyncManage /> : <WalletSyncActivation />}
</SideDrawer>

<Button small event="Manage WalletSync" primary onClick={() => setOpen(true)}>
Manage
{t("walletSync.manage")}
</Button>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as React from "react";
import WalletSyncRow from "../index";

export function WalletSyncPages() {
return (
<>
<div id="modals"></div>
<WalletSyncRow />
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import CounterValueSelect from "./CounterValueSelect";
import LanguageSelect from "./LanguageSelect";
import RegionSelect from "./RegionSelect";
import ThemeSelect from "./ThemeSelect";
import WalletSync from "./WalletSyncRow";
import WalletSync from "./WalletSync";
import PasswordButton from "./PasswordButton";
import PasswordAutoLockSelect from "./PasswordAutoLockSelect";
import SentryLogsButton from "./SentryLogsButton";
Expand Down
12 changes: 12 additions & 0 deletions apps/ledger-live-desktop/static/i18n/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6274,5 +6274,17 @@
"personalizedExpDesc": "Receive personalised recommendations that match your preferences.",
"analyticsDesc": "Enable analytics to help Ledger improve the user experience."
}
},
"walletSync": {
"manage": "Manage",
"activate": {
"title": "Activate wallet sync",
"description": "Create a secure backup of your Ledger Live and synchronize multiple instances of Ledger Live, both on mobile and desktop.",
"cta": "Create a backup"
},
"alreadySync": {
"title": "Already created a back-up on another Device?",
"cta": "Synchronize"
}
}
}
15 changes: 15 additions & 0 deletions apps/ledger-live-desktop/tests/jestSetup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { jest } from "@jest/globals";
import { TextDecoder, TextEncoder } from "util";

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore FIX TS config
jest.mock("@sentry/electron/renderer", () => ({
init: jest.fn(),
setUser: jest.fn(),
captureException: jest.fn(),
addBreadcrumb: jest.fn(),
setTags: jest.fn(),
}));

jest.mock("src/sentry/install", () => ({
init: jest.fn(),
}));

global.TextEncoder = TextEncoder;
// @ts-expect-error weird compatibility
global.TextDecoder = TextDecoder;
14 changes: 9 additions & 5 deletions apps/ledger-live-desktop/tests/testUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { render as rtlRender } from "@testing-library/react";
import { type State } from "~/renderer/reducers";
import createStore from "../src/renderer/createStore";
import dbMiddleware from "../src/renderer/middlewares/db";
import { I18nextProvider } from "react-i18next";
import i18n from "~/renderer/i18n/init";

interface ChildrenProps {
children: JSX.Element;
Expand Down Expand Up @@ -38,11 +40,13 @@ function render(
): RenderReturn {
function Wrapper({ children }: ChildrenProps) {
return (
<Provider store={store}>
<StyleProvider selectedPalette="dark">
<Router>{children}</Router>
</StyleProvider>
</Provider>
<I18nextProvider i18n={i18n}>
<Provider store={store}>
<StyleProvider selectedPalette="dark">
<Router>{children}</Router>
</StyleProvider>
</Provider>
</I18nextProvider>
);
}
return {
Expand Down
3 changes: 2 additions & 1 deletion apps/ledger-live-desktop/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"package.json",
"release-notes.json",
"tests",
"index-types.d.ts"
"index-types.d.ts",
"tests/jestSetup.ts"
]
}

0 comments on commit 879fda4

Please sign in to comment.