From 48bef7d545a517fad93be0bbaf9262ac059368f3 Mon Sep 17 00:00:00 2001 From: Sergey Kintsel Date: Thu, 29 Aug 2024 13:23:18 +0100 Subject: [PATCH] Add additional account import feature --- .../masterPassword/MasterPassword.tsx | 8 +- .../AccountSelectorModal.tsx | 116 +++++++++--------- apps/web/src/components/AccountTile/index.ts | 1 + .../components/AddressTile/AddressTile.tsx | 2 +- apps/web/src/components/Menu/Menu.tsx | 17 +-- .../ImportWallet/ImportWallet.test.tsx | 37 ++++++ .../Onboarding/ImportWallet/ImportWallet.tsx | 21 +++- .../Onboarding/ImportWallet/LedgerTab.tsx | 48 ++++---- .../Onboarding/ImportWallet/SecretKeyTab.tsx | 1 + .../Onboarding/ImportWallet/SeedPhraseTab.tsx | 2 +- .../OnboardOptions/OnboardOptions.tsx | 74 +++++++++++ .../OnboardOptions/OnboardOptionsModal.tsx | 17 +++ .../OnboardWithEmailButton.tsx | 4 +- .../OnboardWithFacebookButton.tsx | 2 +- .../OnboardWithGoogleButton.tsx | 2 +- .../OnboardWithRedditButton.tsx | 2 +- .../OnboardWithTwitterButton.tsx | 2 +- .../Onboarding/OnboardOptions/index.ts | 2 + .../OnboardOptions}/useOnboardWithSocial.tsx | 0 .../SetupPassword/SetupPassword.tsx | 33 +++-- .../src/components/TabSwitch/TabSwitch.tsx | 4 +- apps/web/src/styles/theme.ts | 12 +- apps/web/src/views/Welcome/Welcome.tsx | 60 +-------- packages/state/src/hooks/getAccountData.ts | 2 + 24 files changed, 290 insertions(+), 179 deletions(-) create mode 100644 apps/web/src/components/Onboarding/ImportWallet/ImportWallet.test.tsx create mode 100644 apps/web/src/components/Onboarding/OnboardOptions/OnboardOptions.tsx create mode 100644 apps/web/src/components/Onboarding/OnboardOptions/OnboardOptionsModal.tsx rename apps/web/src/{views/Welcome => components/Onboarding/OnboardOptions}/OnboardWithEmailButton.tsx (85%) rename apps/web/src/{views/Welcome => components/Onboarding/OnboardOptions}/OnboardWithFacebookButton.tsx (91%) rename apps/web/src/{views/Welcome => components/Onboarding/OnboardOptions}/OnboardWithGoogleButton.tsx (91%) rename apps/web/src/{views/Welcome => components/Onboarding/OnboardOptions}/OnboardWithRedditButton.tsx (91%) rename apps/web/src/{views/Welcome => components/Onboarding/OnboardOptions}/OnboardWithTwitterButton.tsx (91%) create mode 100644 apps/web/src/components/Onboarding/OnboardOptions/index.ts rename apps/web/src/{views/Welcome => components/Onboarding/OnboardOptions}/useOnboardWithSocial.tsx (100%) diff --git a/apps/desktop/src/components/Onboarding/masterPassword/MasterPassword.tsx b/apps/desktop/src/components/Onboarding/masterPassword/MasterPassword.tsx index 7ece532bc7..30f3d90f99 100644 --- a/apps/desktop/src/components/Onboarding/masterPassword/MasterPassword.tsx +++ b/apps/desktop/src/components/Onboarding/masterPassword/MasterPassword.tsx @@ -1,6 +1,7 @@ import { useToast } from "@chakra-ui/react"; import { useAsyncActionHandler, + useIsPasswordSet, useRestoreFromMnemonic, useRestoreFromSecretKey, useValidateMasterPassword, @@ -20,15 +21,14 @@ export const MasterPassword = ({ const restoreFromMnemonic = useRestoreFromMnemonic(); const restoreFromSecretKey = useRestoreFromSecretKey(); const checkPassword = useValidateMasterPassword(); - const passwordHasBeenSet = checkPassword !== null; + const passwordHasBeenSet = useIsPasswordSet(); const { isLoading, handleAsyncAction } = useAsyncActionHandler(); const toast = useToast(); const handleSubmit = (password: string) => handleAsyncAction(async () => { - if (passwordHasBeenSet) { - await checkPassword(password); - } + await checkPassword?.(password); + switch (account.type) { case "secret_key": await restoreFromSecretKey(account.secretKey, password, account.label); diff --git a/apps/web/src/components/AccountSelectorModal/AccountSelectorModal.tsx b/apps/web/src/components/AccountSelectorModal/AccountSelectorModal.tsx index 4f42ee567d..178eaa5b54 100644 --- a/apps/web/src/components/AccountSelectorModal/AccountSelectorModal.tsx +++ b/apps/web/src/components/AccountSelectorModal/AccountSelectorModal.tsx @@ -22,16 +22,15 @@ import { ThreeDotsIcon } from "../../assets/icons"; import { useColor } from "../../styles/useColor"; import { AccountTile } from "../AccountTile"; import { ModalCloseButton } from "../CloseButton"; +import { OnboardOptionsModal } from "../Onboarding/OnboardOptions"; import { useCheckVerified } from "../Onboarding/useCheckUnverified"; export const AccountSelectorModal = () => { const accounts = useImplicitAccounts(); const color = useColor(); const getBalance = useGetAccountBalance(); - // TODO: add ConnectOptions onboarding modal - const { onOpen: openOnboardingModal, modalElement } = { modalElement: null, onOpen: () => {} }; - const { onClose } = useDynamicModalContext(); const isVerified = useCheckVerified(); + const { openWith, onClose } = useDynamicModalContext(); const dispatch = useDispatch(); @@ -41,64 +40,61 @@ export const AccountSelectorModal = () => { .value(); return ( - <> - {modalElement} - - - - - - }> - {Object.entries(groupedAccounts).map(([type, accounts]) => ( - -
- - {type.split("_").map(capitalize).join(" ")} - - } - size="xs" - variant="ghost" - /> -
- {accounts.map(account => { - const address = account.address.pkh; - const balance = getBalance(address); - const onClick = () => { - dispatch(accountsActions.setCurrent(address)); - onClose(); - }; + + + + + + }> + {Object.entries(groupedAccounts).map(([type, accounts]) => ( + +
+ + {type.split("_").map(capitalize).join(" ")} + + } + size="xs" + variant="ghost" + /> +
+ {accounts.map(account => { + const address = account.address.pkh; + const balance = getBalance(address); + const onClick = () => { + dispatch(accountsActions.setCurrent(address)); + onClose(); + }; - return ( - - - {isVerified && } - - {balance ? prettyTezAmount(balance) : "\u00A0"} - - - - ); - })} -
- ))} -
+ return ( + + + {isVerified && } + + {balance ? prettyTezAmount(balance) : "\u00A0"} + + + + ); + })} +
+ ))} +
- {isVerified && ( - - )} -
-
- + {isVerified && ( + + )} + + ); }; diff --git a/apps/web/src/components/AccountTile/index.ts b/apps/web/src/components/AccountTile/index.ts index 475d078bf7..5f0f4a08d8 100644 --- a/apps/web/src/components/AccountTile/index.ts +++ b/apps/web/src/components/AccountTile/index.ts @@ -1 +1,2 @@ export * from "./AccountTile"; +export * from "./AccountTileWrapper"; diff --git a/apps/web/src/components/AddressTile/AddressTile.tsx b/apps/web/src/components/AddressTile/AddressTile.tsx index bcf7fc224a..f640bac535 100644 --- a/apps/web/src/components/AddressTile/AddressTile.tsx +++ b/apps/web/src/components/AddressTile/AddressTile.tsx @@ -5,7 +5,7 @@ import { type Address, prettyTezAmount } from "@umami/tezos"; import { AddressTileIcon } from "./AddressTileIcon"; import { useColor } from "../../styles/useColor"; -import { AccountTileWrapper } from "../AccountTile/AccountTileWrapper"; +import { AccountTileWrapper } from "../AccountTile"; import { CopyAddressButton } from "../CopyAddressButton"; /** diff --git a/apps/web/src/components/Menu/Menu.tsx b/apps/web/src/components/Menu/Menu.tsx index 167723cf22..e2934c9688 100644 --- a/apps/web/src/components/Menu/Menu.tsx +++ b/apps/web/src/components/Menu/Menu.tsx @@ -17,19 +17,13 @@ import { SettingsIcon, UserPlusIcon, } from "../../assets/icons"; +import { OnboardOptionsModal } from "../Onboarding/OnboardOptions"; import { useCheckVerified } from "../Onboarding/useCheckUnverified"; -// TODO: Make this work -export const useOnboardingModal = () => ({ - onOpen: () => {}, - modalElement: <>, -}); - export const Menu = () => { const { openWith: openModal } = useDynamicModalContext(); const { openWith: openDrawer } = useDynamicDrawerContext(); const { colorMode, toggleColorMode } = useColorMode(); - const { onOpen: openOnboardingModal, modalElement } = useOnboardingModal(); const isVerified = useCheckVerified(); const colorModeSwitchLabel = colorMode === "light" ? "Light mode" : "Dark mode"; @@ -44,7 +38,7 @@ export const Menu = () => { { label: "Add Account", icon: , - onClick: openOnboardingModal, + onClick: () => openModal(), }, { label: "Save Backup", @@ -86,10 +80,5 @@ export const Menu = () => { ], ]; - return ( - <> - - {modalElement} - - ); + return ; }; diff --git a/apps/web/src/components/Onboarding/ImportWallet/ImportWallet.test.tsx b/apps/web/src/components/Onboarding/ImportWallet/ImportWallet.test.tsx new file mode 100644 index 0000000000..6426b1caa4 --- /dev/null +++ b/apps/web/src/components/Onboarding/ImportWallet/ImportWallet.test.tsx @@ -0,0 +1,37 @@ +import { mockImplicitAccount } from "@umami/core"; +import { addTestAccount, makeStore } from "@umami/state"; + +import { ImportWallet } from "./ImportWallet"; +import { renderInModal, screen, waitFor } from "../../../testUtils"; + +jest.mock("@chakra-ui/react", () => ({ + ...jest.requireActual("@chakra-ui/react"), + useBreakpointValue: jest.fn(map => map["lg"]), +})); + +describe("", () => { + it.each(["Seed Phrase", "Secret Key", "Ledger"])("renders %s tab", async tabName => { + await renderInModal(); + + await waitFor(() => expect(screen.getByText(tabName)).toBeVisible()); + }); + + describe("when the user has not onboarded", () => { + it("renders the Backup tab", async () => { + await renderInModal(); + + await waitFor(() => expect(screen.getByText("Backup")).toBeVisible()); + }); + }); + + describe("when the user has onboarded", () => { + it("does not render the Backup tab", async () => { + const store = makeStore(); + addTestAccount(store, mockImplicitAccount(0)); + + await renderInModal(, store); + + expect(screen.queryByText("Backup")).not.toBeInTheDocument(); + }); + }); +}); diff --git a/apps/web/src/components/Onboarding/ImportWallet/ImportWallet.tsx b/apps/web/src/components/Onboarding/ImportWallet/ImportWallet.tsx index 03784e9c78..04fe1a0acb 100644 --- a/apps/web/src/components/Onboarding/ImportWallet/ImportWallet.tsx +++ b/apps/web/src/components/Onboarding/ImportWallet/ImportWallet.tsx @@ -9,6 +9,7 @@ import { TabPanels, Tabs, } from "@chakra-ui/react"; +import { useImplicitAccounts } from "@umami/state"; import { ImportBackupTab } from "./ImportBackupTab"; import { LedgerTab } from "./LedgerTab"; @@ -19,8 +20,12 @@ import { useColor } from "../../../styles/useColor"; import { ModalCloseButton } from "../../CloseButton"; import { TabSwitch } from "../../TabSwitch/TabSwitch"; +const BEFORE_ONBOARDING_OPTIONS = ["Seed Phrase", "Secret Key", "Backup", "Ledger"]; +const AFTER_ONBOARDING_OPTIONS = ["Seed Phrase", "Secret Key", "Ledger"]; + export const ImportWallet = () => { const color = useColor(); + const hasOnboarded = useImplicitAccounts().length > 0; return ( @@ -31,20 +36,28 @@ export const ImportWallet = () => { Import Wallet + - + + - - - + + {!hasOnboarded && ( + + + + )} + diff --git a/apps/web/src/components/Onboarding/ImportWallet/LedgerTab.tsx b/apps/web/src/components/Onboarding/ImportWallet/LedgerTab.tsx index d25919539b..4e75e73325 100644 --- a/apps/web/src/components/Onboarding/ImportWallet/LedgerTab.tsx +++ b/apps/web/src/components/Onboarding/ImportWallet/LedgerTab.tsx @@ -1,4 +1,4 @@ -import { Button, Flex, Heading, Link, ListItem, OrderedList } from "@chakra-ui/react"; +import { Button, Flex, Heading, Link, ListItem, OrderedList, Text } from "@chakra-ui/react"; import { type Curves } from "@taquito/signer"; import { useDynamicModalContext, useMultiForm } from "@umami/components"; import { @@ -76,32 +76,38 @@ export const LedgerTab = () => { 3 - Ensure your ledger has the{" "} - - latest firmware - {" "} - version + + + Ensure your ledger has the{" "} + + latest firmware + {" "} + version + 4 - Install & open the{" "} - - Tezos Wallet - {" "} - app on your ledger + + + Install & open the{" "} + + Tezos Wallet + {" "} + app on your ledger + diff --git a/apps/web/src/components/Onboarding/ImportWallet/SecretKeyTab.tsx b/apps/web/src/components/Onboarding/ImportWallet/SecretKeyTab.tsx index 54dc0d36ff..f8fa7e2347 100644 --- a/apps/web/src/components/Onboarding/ImportWallet/SecretKeyTab.tsx +++ b/apps/web/src/components/Onboarding/ImportWallet/SecretKeyTab.tsx @@ -42,6 +42,7 @@ export const SecretKeyTab = () => { Secret Key