diff --git a/src/components/PasswordAlert.tsx b/src/components/PasswordAlert.tsx index 435e292..a3abe8c 100644 --- a/src/components/PasswordAlert.tsx +++ b/src/components/PasswordAlert.tsx @@ -1,4 +1,4 @@ -import { useRef } from 'react'; +import { useRef, useState } from 'react'; import { Form } from 'react-hook-form'; import { @@ -21,12 +21,19 @@ import usePassword from '../store/usePassword'; import PasswordInput from './PasswordInput'; function PasswordAlert(): JSX.Element { + const [isLoading, setIsLoading] = useState(false); const { form } = usePassword(); const cancelRef = useRef(null); if (!form.register.password || !form.register.remember) { throw new Error('PasswordAlert: password or remember is not registered'); } + const onSubmit = async () => { + setIsLoading(true); + await form.onSubmit(); + setIsLoading(false); + }; + return ( - {form.actionLabel} + {isLoading ? 'Checking password...' : form.actionLabel} diff --git a/src/screens/UnlockScreen.tsx b/src/screens/UnlockScreen.tsx index b64979e..fe0c742 100644 --- a/src/screens/UnlockScreen.tsx +++ b/src/screens/UnlockScreen.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import { Form, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; @@ -32,8 +33,10 @@ function UnlockScreen(): JSX.Element { reset, formState: { errors }, } = useForm(); + const [isLoading, setIsLoading] = useState(false); const submit = handleSubmit(async (data) => { + setIsLoading(true); const success = await unlockWallet(data.password); if (!success) { setError('password', { type: 'value', message: 'Invalid password' }); @@ -41,6 +44,7 @@ function UnlockScreen(): JSX.Element { } setValue('password', ''); reset(); + setIsLoading(false); navigate('/wallet'); }); @@ -64,8 +68,14 @@ function UnlockScreen(): JSX.Element { {errors.password?.message} - diff --git a/src/screens/welcome/ImportScreen.tsx b/src/screens/welcome/ImportScreen.tsx index afc175e..27255e4 100644 --- a/src/screens/welcome/ImportScreen.tsx +++ b/src/screens/welcome/ImportScreen.tsx @@ -19,6 +19,7 @@ import BackButton from '../../components/BackButton'; import PasswordInput from '../../components/PasswordInput'; import useWallet from '../../store/useWallet'; import { WalletFile } from '../../types/wallet'; +import { postpone } from '../../utils/promises'; type FormValues = { password: string; @@ -38,6 +39,7 @@ function ImportScreen(): JSX.Element { } = useForm(); const { openWallet } = useWallet(); const navigate = useNavigate(); + const [isLoading, setIsLoading] = useState(false); const readFile = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; @@ -75,11 +77,18 @@ function ImportScreen(): JSX.Element { setError('root', { type: 'manual', message: 'No wallet file loaded' }); return; } - const success = await openWallet(walletFileContent, password); + setIsLoading(true); + const success = await postpone( + // We need to postpone it for one tick + // to allow component to re-render + () => openWallet(walletFileContent, password), + 1 + ); if (!success) { setError('password', { type: 'value', message: 'Invalid password' }); return; } + setIsLoading(false); navigate('/wallet'); }); @@ -135,8 +144,9 @@ function ImportScreen(): JSX.Element { mt={4} width="100%" onClick={onSubmit} + disabled={isLoading} > - Import wallet + {isLoading ? 'Importing...' : 'Import wallet'} diff --git a/src/store/usePassword.ts b/src/store/usePassword.ts index 31bd1c1..0731887 100644 --- a/src/store/usePassword.ts +++ b/src/store/usePassword.ts @@ -13,6 +13,7 @@ import { useDisclosure } from '@chakra-ui/react'; import { MINUTE } from '../utils/constants'; import { noop } from '../utils/func'; +import { postpone } from '../utils/promises'; const REMEMBER_PASSWORD_TIME = 5 * MINUTE; @@ -123,16 +124,20 @@ const usePassword = (): UsePasswordReturnType => { ); return; } - try { - const res = await passwordCallback(password); - setPassword(password, remember); - _onClose(); - setValue('password', ''); - reset(); - eventEmitter.emit('success', res); - } catch (err) { - setError('password', { message: 'Incorrect password' }); - } + await postpone(async () => { + // We need to postpone it for one tick to allow + // the form to re-render before start checking the password + try { + const res = await passwordCallback(password); + setPassword(password, remember); + _onClose(); + setValue('password', ''); + reset(); + eventEmitter.emit('success', res); + } catch (err) { + setError('password', { message: 'Incorrect password' }); + } + }, 1); }); const onClose = () => { diff --git a/src/utils/promises.ts b/src/utils/promises.ts new file mode 100644 index 0000000..a040682 --- /dev/null +++ b/src/utils/promises.ts @@ -0,0 +1,11 @@ +export const delay = (ms: number) => + new Promise((resolve) => { + setTimeout(resolve, ms); + }); + +export const postpone = (fn: () => T, ms: number): Promise => + new Promise((resolve) => { + setTimeout(async () => { + resolve(await fn()); + }, ms); + });