diff --git a/packages/neuron-ui/package.json b/packages/neuron-ui/package.json index 82ca18157c..c832d47fd1 100644 --- a/packages/neuron-ui/package.json +++ b/packages/neuron-ui/package.json @@ -54,6 +54,7 @@ "@ckb-lumos/rpc": "0.21.1", "@ckb-lumos/base": "0.21.1", "@ckb-lumos/codec": "0.21.1", + "@ckb-lumos/hd": "0.21.1", "@ckb-lumos/helpers": "0.21.1", "@ckb-lumos/config-manager": "0.21.1", "@ckb-lumos/common-scripts": "0.21.1", diff --git a/packages/neuron-ui/src/components/WalletWizard/hooks.ts b/packages/neuron-ui/src/components/WalletWizard/hooks.ts index c52c3867bd..c073cc8661 100644 --- a/packages/neuron-ui/src/components/WalletWizard/hooks.ts +++ b/packages/neuron-ui/src/components/WalletWizard/hooks.ts @@ -1,12 +1,34 @@ import { useState, useCallback } from 'react' +const MNEMONIC_SENTENCE_WORDS = 12 + export const useInputWords = () => { - const [inputsWords, setInputsWords] = useState(new Array(12).fill('')) + const [inputsWords, setInputsWords] = useState(new Array(MNEMONIC_SENTENCE_WORDS).fill('')) const onChangeInput = useCallback( - (e: React.ChangeEvent) => { + ( + e: + | React.ChangeEvent + | { + target: { + dataset: { idx: string } + value: string + } + } + ) => { const idx = Number(e.target.dataset.idx) if (Number.isNaN(idx)) return const { value } = e.target + if (Number(idx) === 0) { + const list = value + .trim() + .replace(/[^0-9a-z]+/g, ' ') + .split(' ') + if (list.length === MNEMONIC_SENTENCE_WORDS) { + setInputsWords(list) + return + } + } + setInputsWords(v => { const newWords = [...v] newWords[idx] = value diff --git a/packages/neuron-ui/src/components/WalletWizard/index.tsx b/packages/neuron-ui/src/components/WalletWizard/index.tsx index 384492854a..b4c7ab61b0 100644 --- a/packages/neuron-ui/src/components/WalletWizard/index.tsx +++ b/packages/neuron-ui/src/components/WalletWizard/index.tsx @@ -20,7 +20,7 @@ import i18n from 'utils/i18n' import MnemonicInput from 'widgets/MnemonicInput' import ReplaceDuplicateWalletDialog, { useReplaceDuplicateWallet } from 'components/ReplaceDuplicateWalletDialog' import Alert from 'widgets/Alert' -import { Loading } from 'widgets/Icons/icon' +import { Loading, SuccessInfo, Error as ErrorIcon } from 'widgets/Icons/icon' import TextField from 'widgets/TextField' import { showGlobalAlertDialog, useDispatch } from 'states' import { importedWalletDialogShown } from 'services/localCache' @@ -182,19 +182,17 @@ const Welcome = ({ rootPath = '/wizard/', wallets = [], dispatch }: WizardElemen Welcome.displayName = 'Welcome' -const typeHits: Record = { - [MnemonicAction.Create]: 'wizard.write-down-seed', - [MnemonicAction.Verify]: 'wizard.input-seed-verify', - [MnemonicAction.Import]: '', -} - const Mnemonic = ({ state = initState, rootPath = '/wizard/', dispatch }: WizardElementProps) => { const { generated, imported } = state const navigate = useNavigate() const { type = MnemonicAction.Create } = useParams<{ type: MnemonicAction }>() const [t] = useTranslation() const isCreate = type === MnemonicAction.Create - const message = isCreate ? 'wizard.your-wallet-seed-is' : 'wizard.input-your-seed' + const message = { + [MnemonicAction.Create]: 'wizard.your-wallet-seed-is', + [MnemonicAction.Verify]: 'wizard.replenish-your-seed', + [MnemonicAction.Import]: 'wizard.input-your-seed', + }[type] const { inputsWords, onChangeInput, setInputsWords } = useInputWords() const [searchParams] = useSearchParams() const disableNext = @@ -202,6 +200,8 @@ const Mnemonic = ({ state = initState, rootPath = '/wizard/', dispatch }: Wizard (type === MnemonicAction.Verify && generated !== inputsWords.join(' ')) const [step, changeStep] = useState(0) + const [blankIndexes, setBlankIndexes] = useState([]) + useEffect(() => { if (type === MnemonicAction.Create) { generateMnemonic().then(res => { @@ -210,7 +210,15 @@ const Mnemonic = ({ state = initState, rootPath = '/wizard/', dispatch }: Wizard type: 'generated', payload: res.result, }) - setInputsWords(new Array(12).fill('')) + const uniqueRandomArray = new Set() + while (uniqueRandomArray.size < 3) { + const randomInt = Math.floor(Math.random() * 12) + uniqueRandomArray.add(randomInt) + } + const nums = [...uniqueRandomArray] + const list = res.result.split(' ').map((item: string, index: number) => (nums.includes(index) ? '' : item)) + setBlankIndexes(nums) + setInputsWords(list) } }) } else { @@ -219,7 +227,7 @@ const Mnemonic = ({ state = initState, rootPath = '/wizard/', dispatch }: Wizard payload: '', }) } - }, [dispatch, type, navigate]) + }, [dispatch, type, navigate, setBlankIndexes]) const globalDispatch = useDispatch() @@ -285,17 +293,32 @@ const Mnemonic = ({ state = initState, rootPath = '/wizard/', dispatch }: Wizard )}
{t(message)}
- {type === MnemonicAction.Import ? ( - - ) : ( -
{t(typeHits[type])}
+ {type === MnemonicAction.Import && } + {type === MnemonicAction.Create && ( +
+
+ + {t('wizard.handwritten-recommended')} +
+
+ + {t('wizard.do-not-copy')} +
+
+ + {t('wizard.do-not-save-scrrenshots')} +
+
)} + {type === MnemonicAction.Verify &&
{t('wizard.input-seed-verify')}
} + {type === MnemonicAction.Import &&
{t('wizard.input-seed-first-empty-space')}
}
+ ))} +
+ ) : null} ))}