-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #27 from CoongCrafts/coong/feature/revealSecretPhrase
Coong/feature/reveal-secret-phrase
- Loading branch information
Showing
18 changed files
with
503 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,85 +1,93 @@ | ||
{ | ||
"Welcome to Coong": "", | ||
"Welcome to Coong Wallet!": "", | ||
"This page should be loaded inside an iframe!": "", | ||
"If you open this page by accident, it's safe to close it now.": "", | ||
"15 minutes": "", | ||
"30 minutes": "", | ||
"5 minutes": "", | ||
"A multichain crypto wallet for Polkadot & Kusama ecosystem": "A multichain crypto wallet <br /> for <strong>Polkadot & Kusama</strong> ecosystem", | ||
"Set up your Coong wallet now": "", | ||
"Create New Wallet": "", | ||
"Restore existing wallet": "", | ||
"Coming soon": "", | ||
"First, choose your wallet password": "", | ||
"Welcome back": "", | ||
"Your password will be used to encrypt accounts as well as unlock the wallet, make sure to pick a strong & easy-to-remember password": "Your password will be used to <strong>encrypt accounts as well as unlock the wallet</strong>, make sure to pick a <strong>strong & easy-to-remember</strong> password.", | ||
"Unlock your wallet": "", | ||
"Wallet password": "", | ||
"Next": "", | ||
"Next, confirm your wallet password": "", | ||
"Confirm wallet password": "", | ||
"Unlock": "", | ||
"Write down the below 12 words and keep it in a safe place.": "", | ||
"Back": "", | ||
"Finish": "", | ||
"I have backed up my recovery phrase": "", | ||
"Finally, back up your secret recovery phrase": "", | ||
"AccountNameRequired": "Account name is required", | ||
"AccountNameUsed": "Account name is already picked", | ||
"AccountNotFound": "Account not found", | ||
"Accounts": "", | ||
"Search by name": "", | ||
"Address copied!": "", | ||
"Address format": "", | ||
"New Account": "", | ||
"Create new account": "", | ||
"An application, self-identifying as request app name is requesting access your wallet from origin.": "An application, self-identifying as <strong>{{appName}}</strong> is requesting access your wallet from <strong>{{origin}}</strong>.", | ||
"Auto-lock wallet after": "", | ||
"Back": "", | ||
"Backup secret recovery phrase": "", | ||
"Cancel": "", | ||
"Choose a name for your new account": "", | ||
"New account name": "", | ||
"Click to copy address": "", | ||
"Close settings": "", | ||
"Coming soon": "", | ||
"Confirm wallet password": "", | ||
"Connect": "", | ||
"Copied!": "", | ||
"Copy to clipboard": "", | ||
"Create": "", | ||
"Cancel": "", | ||
"My first account": "", | ||
"Settings": "", | ||
"Theme Mode": "", | ||
"Language": "", | ||
"Create New Wallet": "", | ||
"Create new account": "", | ||
"Create your first account now!": "", | ||
"Dark": "", | ||
"System": "", | ||
"Deselect all": "", | ||
"Enter your wallet password to continue": "", | ||
"Finally, back up your secret recovery phrase": "", | ||
"Finish": "", | ||
"First, choose your wallet password": "", | ||
"I have backed up my recovery phrase": "", | ||
"If you open this page by accident, it's safe to close it now.": "", | ||
"InternalError": "Internal error", | ||
"Invalid Request": "", | ||
"Invalid request": "", | ||
"InvalidMessageFormat": "Invalid message format", | ||
"KeypairNotFound": "Keypair not found", | ||
"KeyringLocked": "The keyring is locked, please unlock the wallet first", | ||
"KeyringNotInitialized": "Keyring is not initialized", | ||
"Language": "", | ||
"Light": "", | ||
"Lock the wallet": "", | ||
"Make sure you are in a safe place.": "", | ||
"My first account": "", | ||
"New Account": "", | ||
"New account name": "", | ||
"Next": "", | ||
"Next, confirm your wallet password": "", | ||
"No accounts found in wallet": "", | ||
"No accounts meet search query:": "", | ||
"No accounts selected": "", | ||
"Only connect if you trust the application": "", | ||
"Open settings": "", | ||
"Close settings": "", | ||
"Password does not match": "", | ||
"Password's too short": "", | ||
"PasswordIncorrect": "Password incorrect", | ||
"PasswordRequired": "Password required", | ||
"Reset wallet": "", | ||
"Wallet Access Request": "", | ||
"An application, self-identifying as request app name is requesting access your wallet from origin.": "An application, self-identifying as <strong>{{appName}}</strong> is requesting access your wallet from <strong>{{origin}}</strong>.", | ||
"Setup your Coong wallet now to connect": "", | ||
"Restore existing wallet": "", | ||
"Search by name": "", | ||
"Select all": "", | ||
"Select the accounts you'd like to connect": "", | ||
"Only connect if you trust the application": "", | ||
"Connect": "", | ||
"Set up wallet": "", | ||
"Set up new wallet": "", | ||
"Password's too short": "", | ||
"Type again your chosen password to ensure you remember it.": "", | ||
"Password does not match": "", | ||
"Select all": "", | ||
"Deselect all": "", | ||
"No accounts selected": "", | ||
"account(s) selected": "", | ||
"Invalid Request": "", | ||
"Set up wallet": "", | ||
"Set up your Coong wallet now": "", | ||
"Settings": "", | ||
"Setup your Coong wallet now to connect": "", | ||
"System": "", | ||
"Theme Mode": "", | ||
"This page should be loaded inside an iframe!": "", | ||
"This page should not be open directly!": "", | ||
"No accounts meet search query:": "", | ||
"No accounts found in wallet": "", | ||
"Create your first account now!": "", | ||
"Click to copy address": "", | ||
"Address copied!": "", | ||
"Auto-lock wallet after": "", | ||
"Type again your chosen password to ensure you remember it.": "", | ||
"UnknownRequest": "Unknown request", | ||
"UnknownRequestOrigin": "Unknown request origin", | ||
"InvalidMessageFormat": "Invalid message format", | ||
"KeypairNotFound": "Keypair not found", | ||
"AccountNotFound": "Account not found", | ||
"KeyringNotInitialized": "Keyring is not initialized", | ||
"PasswordIncorrect": "Password incorrect", | ||
"PasswordRequired": "Password required", | ||
"Unlock": "", | ||
"Unlock your wallet": "", | ||
"View Secret Recovery Phrase": "", | ||
"Wallet Access Request": "", | ||
"Wallet password": "", | ||
"WalletLocked": "The wallet is locked, please unlock it first", | ||
"KeyringLocked": "The keyring is locked, please unlock the wallet first", | ||
"AccountNameRequired": "Account name is required", | ||
"AccountNameUsed": "Account name is already picked", | ||
"InternalError": "Internal error", | ||
"Invalid request": "", | ||
"5 minutes": "", | ||
"15 minutes": "", | ||
"30 minutes": "" | ||
} | ||
"Welcome back": "", | ||
"Welcome to Coong": "", | ||
"Welcome to Coong Wallet!": "", | ||
"Write down the below 12 words and keep it in a safe place.": "", | ||
"You are about to reveal the secret recovery phrase which give access to your accounts and funds.": "", | ||
"Your password will be used to encrypt accounts as well as unlock the wallet, make sure to pick a strong & easy-to-remember password": "Your password will be used to <strong>encrypt accounts as well as unlock the wallet</strong>, make sure to pick a <strong>strong & easy-to-remember</strong> password.", | ||
"Your wallet password": "", | ||
"account(s) selected": "" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
packages/ui/src/components/shared/settings/BackupSecretPhraseDialog/ShowingSecretPhrase.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { FC, useState } from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { useDispatch, useSelector } from 'react-redux'; | ||
import { useAsync, useCopyToClipboard } from 'react-use'; | ||
import ContentCopyIcon from '@mui/icons-material/ContentCopy'; | ||
import { Button, DialogContentText } from '@mui/material'; | ||
import { useWalletState } from 'providers/WalletStateProvider'; | ||
import { settingsDialogActions } from 'redux/slices/settings-dialog'; | ||
import { RootState } from 'redux/store'; | ||
import { Props } from 'types'; | ||
|
||
const ShowingSecretPhrase: FC<Props> = () => { | ||
const { keyring } = useWalletState(); | ||
const { verifiedPassword } = useSelector((state: RootState) => state.settingsDialog); | ||
const [secretPhrase, setSecretPhrase] = useState(''); | ||
const [_, copyToClipboard] = useCopyToClipboard(); | ||
const { t } = useTranslation(); | ||
const [copyButtonLabel, setCopyButtonLabel] = useState('Copy to clipboard'); | ||
const dispatch = useDispatch(); | ||
|
||
const doBack = () => { | ||
dispatch(settingsDialogActions.resetState()); | ||
}; | ||
|
||
const doCopy = () => { | ||
copyToClipboard(secretPhrase); | ||
setCopyButtonLabel('Copied!'); | ||
setTimeout(() => setCopyButtonLabel('Copy to clipboard'), 5e3); | ||
}; | ||
|
||
useAsync(async () => { | ||
try { | ||
setSecretPhrase(await keyring.getRawMnemonic(verifiedPassword!)); | ||
} catch (e: any) { | ||
console.error(e.message); | ||
} | ||
}); | ||
|
||
return ( | ||
<> | ||
<DialogContentText className='my-8 p-4 bg-black/10 dark:bg-white/15'>{secretPhrase}</DialogContentText> | ||
<div className='mt-4 flex gap-4'> | ||
<Button variant='text' onClick={doBack}> | ||
{t<string>('Back')} | ||
</Button> | ||
<Button variant='contained' onClick={doCopy} startIcon={<ContentCopyIcon />} fullWidth> | ||
{t<string>(copyButtonLabel)} | ||
</Button> | ||
</div> | ||
</> | ||
); | ||
}; | ||
|
||
export default ShowingSecretPhrase; |
67 changes: 67 additions & 0 deletions
67
packages/ui/src/components/shared/settings/BackupSecretPhraseDialog/VerifyingPassword.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { ChangeEvent, FC, FormEvent, useState } from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { useDispatch } from 'react-redux'; | ||
import { Button, DialogContentText, TextField } from '@mui/material'; | ||
import EmptySpace from 'components/shared/misc/EmptySpace'; | ||
import { useWalletState } from 'providers/WalletStateProvider'; | ||
import { settingsDialogActions } from 'redux/slices/settings-dialog'; | ||
import { Props } from 'types'; | ||
|
||
const VerifyingPassword: FC<Props> = () => { | ||
const { keyring } = useWalletState(); | ||
const [password, setPassword] = useState(''); | ||
const [validation, setValidation] = useState(''); | ||
const dispatch = useDispatch(); | ||
const { t } = useTranslation(); | ||
|
||
const handleChange = (event: ChangeEvent<HTMLInputElement>) => { | ||
setPassword(event.currentTarget.value); | ||
setValidation(''); | ||
}; | ||
|
||
const doVerify = async (event: FormEvent) => { | ||
event.preventDefault(); | ||
if (!password) { | ||
return; | ||
} | ||
|
||
try { | ||
await keyring.verifyPassword(password); | ||
dispatch(settingsDialogActions.setVerifiedPassword(password)); | ||
} catch (e: any) { | ||
setValidation(t<string>(e.message)); | ||
} | ||
}; | ||
|
||
const doBack = () => { | ||
dispatch(settingsDialogActions.resetState()); | ||
}; | ||
|
||
return ( | ||
<> | ||
<DialogContentText className='mt-4 mb-4'>{t<string>('Enter your wallet password to continue')}</DialogContentText> | ||
<form onSubmit={doVerify} noValidate> | ||
<TextField | ||
type='password' | ||
value={password} | ||
label={t<string>('Your wallet password')} | ||
fullWidth | ||
autoFocus | ||
error={!!validation} | ||
helperText={validation || <EmptySpace />} | ||
onChange={handleChange} | ||
/> | ||
<div className='mt-2.5 flex gap-4'> | ||
<Button variant='text' onClick={doBack}> | ||
{t<string>('Back')} | ||
</Button> | ||
<Button type='submit' disabled={!password} fullWidth variant='contained'> | ||
{t<string>('View Secret Recovery Phrase')} | ||
</Button> | ||
</div> | ||
</form> | ||
</> | ||
); | ||
}; | ||
|
||
export default VerifyingPassword; |
78 changes: 78 additions & 0 deletions
78
...nents/shared/settings/BackupSecretPhraseDialog/__test__/BackupSecretPhraseDialog.spec.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import Keyring from '@coong/keyring'; | ||
import { initializeKeyring, newUser, render, screen, UserEvent, waitFor } from '__tests__/testUtils'; | ||
import SettingsWalletButton from 'components/shared/settings/SettingsWalletButton'; | ||
import { SettingsDialogScreen } from 'types'; | ||
|
||
let user: UserEvent; | ||
vi.spyOn(window, 'prompt').mockImplementation(() => ''); | ||
beforeEach(async () => { | ||
user = newUser(); | ||
|
||
render(<SettingsWalletButton />, { | ||
preloadedState: { | ||
app: { seedReady: true, ready: true, locked: false }, | ||
settingsDialog: { settingsDialogScreen: SettingsDialogScreen.BackupSecretPhrase }, | ||
}, | ||
}); | ||
|
||
const settingsButton = screen.getByTitle('Open settings'); | ||
await user.click(settingsButton); | ||
}); | ||
|
||
describe('when verifying password', () => { | ||
it('should show the content of BackupSecretPhraseDialog', async () => { | ||
await waitFor(() => { | ||
expect(screen.getByText(/Backup secret recovery phrase/)).toBeInTheDocument(); | ||
expect(screen.getByText(/reveal the secret recovery phrase/)).toBeInTheDocument(); | ||
expect(screen.getByRole('button', { name: /View Secret Recovery Phrase/ })).toBeInTheDocument(); | ||
expect(screen.getByRole('button', { name: /Back/ })).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it('should enable `View Secret Recovery Phrase` button when typing password', async () => { | ||
const passwordField = await screen.findByLabelText(/Your wallet password/); | ||
await user.type(passwordField, 'password'); | ||
|
||
expect(await screen.findByRole('button', { name: /View Secret Recovery Phrase/ })).toBeEnabled(); | ||
}); | ||
|
||
it('should show error message on submitting incorrect password', async () => { | ||
await initializeKeyring(); | ||
|
||
const passwordField = await screen.findByLabelText(/Your wallet password/); | ||
await user.type(passwordField, 'incorrect-password'); | ||
|
||
const viewSecretPhraseButton = await screen.findByRole('button', { name: /View Secret Recovery Phrase/ }); | ||
await user.click(viewSecretPhraseButton); | ||
|
||
expect(await screen.findByText(/Password incorrect/)).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
describe('when showing secret phrase', () => { | ||
let keyring: Keyring; | ||
beforeEach(async () => { | ||
keyring = await initializeKeyring(); | ||
|
||
const passwordField = await screen.findByLabelText(/Your wallet password/); | ||
await user.type(passwordField, 'supersecretpassword'); | ||
|
||
const viewSecretPhraseButton = await screen.findByRole('button', { name: /View Secret Recovery Phrase/ }); | ||
await user.click(viewSecretPhraseButton); | ||
}); | ||
|
||
it('should show `ShowingSecretPhrase` when submitting correct password', async () => { | ||
const rawMnemonic = await keyring.getRawMnemonic('supersecretpassword'); | ||
|
||
expect(await screen.findByText(rawMnemonic)).toBeInTheDocument(); | ||
expect(await screen.findByRole('button', { name: /Copy to clipboard/ })).toBeInTheDocument(); | ||
expect(await screen.findByRole('button', { name: /Back/ })).toBeInTheDocument(); | ||
}); | ||
|
||
it('should show change button label to `Copied!` when clicking on `Copy to clipboard` button', async () => { | ||
const copyToClipboardButton = await screen.findByRole('button', { name: /Copy to clipboard/ }); | ||
await user.click(copyToClipboardButton); | ||
|
||
expect(await screen.findByRole('button', { name: /Copied!/ })).toBeInTheDocument(); | ||
}); | ||
}); |
Oops, something went wrong.