From ac383dcdd8f3bee4e63e3236537c5e945c2edd69 Mon Sep 17 00:00:00 2001 From: Zachary Johnson Date: Thu, 8 Feb 2024 20:08:59 -0500 Subject: [PATCH] feat: check for existing wallet (#720) --- src/components/CreateWallet.test.tsx | 22 ++++++++++++++++++++++ src/components/WalletCreationForm.tsx | 25 +++++++++++++++++++++++-- src/i18n/locales/en/translation.json | 1 + 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/components/CreateWallet.test.tsx b/src/components/CreateWallet.test.tsx index 967d6888..96f9906b 100644 --- a/src/components/CreateWallet.test.tsx +++ b/src/components/CreateWallet.test.tsx @@ -13,6 +13,7 @@ jest.mock('../libs/JmWalletApi', () => ({ getGetinfo: jest.fn(), getSession: jest.fn(), postWalletCreate: jest.fn(), + getWalletAll: jest.fn(), })) const NOOP = () => {} @@ -38,6 +39,7 @@ describe('', () => { const neverResolvingPromise = new Promise(() => {}) ;(apiMock.getGetinfo as jest.Mock).mockReturnValue(neverResolvingPromise) ;(apiMock.getSession as jest.Mock).mockReturnValue(neverResolvingPromise) + ;(apiMock.getWalletAll as jest.Mock).mockReturnValue(neverResolvingPromise) }) it('should display alert when rescanning is active', async () => { @@ -87,6 +89,26 @@ describe('', () => { expect(await screen.findByText('create_wallet.feedback_invalid_password_confirm')).toBeVisible() }) + it('should show validation message to user if duplicate wallet name', async () => { + ;(apiMock.getWalletAll as jest.Mock).mockReturnValue( + Promise.resolve({ + ok: true, + json: () => Promise.resolve({ wallets: [`${testWalletName}.jmdat`] }), + }), + ) + setup({}) + + expect(await screen.queryByText('create_wallet.feedback_wallet_name_already_exists')).not.toBeInTheDocument() + + await user.type(screen.getByPlaceholderText('create_wallet.placeholder_wallet_name'), testWalletName) + await user.type(screen.getByPlaceholderText('create_wallet.placeholder_password'), testWalletPassword) + await user.type(screen.getByPlaceholderText('create_wallet.placeholder_password_confirm'), testWalletPassword) + + await user.click(screen.getByText('create_wallet.button_create')) + + expect(await screen.findByText('create_wallet.feedback_wallet_name_already_exists')).toBeVisible() + }) + it('should not submit form if wallet name contains invalid characters', async () => { setup({}) diff --git a/src/components/WalletCreationForm.tsx b/src/components/WalletCreationForm.tsx index db7ba6e5..413c5005 100644 --- a/src/components/WalletCreationForm.tsx +++ b/src/components/WalletCreationForm.tsx @@ -1,10 +1,11 @@ -import { useCallback } from 'react' +import { useCallback, useEffect, useState } from 'react' import * as rb from 'react-bootstrap' import { useTranslation } from 'react-i18next' import { Formik, FormikErrors } from 'formik' import Sprite from './Sprite' import { JM_WALLET_FILE_EXTENSION, sanitizeWalletName } from '../utils' import styles from './WalletCreationForm.module.css' +import * as Api from '../libs/JmWalletApi' export interface CreateWalletFormValues { walletName: string @@ -36,14 +37,34 @@ const WalletCreationForm = ({ onCancel, onSubmit, }: WalletCreationFormProps) => { + const [walletList, setWalletList] = useState(null) const { t, i18n } = useTranslation() + useEffect(() => { + const abortCtrl = new AbortController() + + Api.getWalletAll({ signal: abortCtrl.signal }) + .then((res) => (res.ok ? res.json() : Api.Helper.throwError(res, t('wallets.error_loading_failed')))) + .then((data) => { + if (abortCtrl.signal.aborted) return + setWalletList(data.wallets) + }) + .catch(() => { + // do nothing on purpose + }) + + return () => abortCtrl.abort() + }, [t]) + const validate = useCallback( (values: CreateWalletFormValues) => { const errors = {} as FormikErrors if (!values.walletName || !validateWalletName(values.walletName)) { errors.walletName = t('create_wallet.feedback_invalid_wallet_name') } + if (walletList && walletList.includes(`${values.walletName}.jmdat`)) { + errors.walletName = t('create_wallet.feedback_wallet_name_already_exists') + } if (!values.password) { errors.password = t('create_wallet.feedback_invalid_password') } @@ -52,7 +73,7 @@ const WalletCreationForm = ({ } return errors }, - [t], + [t, walletList], ) return ( diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 4965e2c4..861aa5ff 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -131,6 +131,7 @@ "label_wallet_name": "Wallet name", "placeholder_wallet_name": "Your Wallet...", "feedback_invalid_wallet_name": "Please choose a valid wallet name: Use only letters, numbers, underscores or hyphens.", + "feedback_wallet_name_already_exists": "Please choose another wallet name. This one is already in use.", "label_password": "Password to unlock the wallet", "placeholder_password": "Choose a secure password...", "feedback_invalid_password": "Please set a password.",