Align confirmation dialog with the UI (#3585)
* Align confirmation dialog with the UI * Padding for all parts * Align confirmation on derive * Combine modals * linting
This commit is contained in:
parent
eab82d853f
commit
0b6c295789
@ -20,8 +20,8 @@ import { keyExtractSuri, mnemonicGenerate, mnemonicValidate, randomAsU8a } from
|
||||
import { getEnvironment } from '@polkadot/react-api/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import PasswordInput from '../Accounts/PasswordInput';
|
||||
import CreateConfirmation from './CreateConfirmation';
|
||||
import PasswordInput from './PasswordInput';
|
||||
|
||||
interface Props extends ModalProps {
|
||||
className?: string;
|
||||
@ -178,6 +178,7 @@ function Create ({ className = '', onClose, onStatusChange, seed: propsSeed, typ
|
||||
const [{ isNameValid, name }, setName] = useState({ isNameValid: false, name: '' });
|
||||
const [{ isPasswordValid, password }, setPassword] = useState({ isPasswordValid: false, password: '' });
|
||||
const isValid = !!address && !deriveError && isNameValid && isPasswordValid && isSeedValid;
|
||||
|
||||
const seedOpt = useMemo(() => (
|
||||
isDevelopment
|
||||
? [{ text: t<string>('Development'), value: 'dev' }]
|
||||
@ -232,13 +233,12 @@ function Create ({ className = '', onClose, onStatusChange, seed: propsSeed, typ
|
||||
const options = { genesisHash: isDevelopment ? undefined : api.genesisHash.toString(), name: name.trim() };
|
||||
const status = createAccount(`${seed}${derivePath}`, pairType, options, password, t<string>('created account'));
|
||||
|
||||
toggleConfirmation();
|
||||
onStatusChange(status);
|
||||
setIsBusy(false);
|
||||
onClose();
|
||||
}, 0);
|
||||
},
|
||||
[api, derivePath, isDevelopment, isValid, name, onClose, onStatusChange, pairType, password, seed, t, toggleConfirmation]
|
||||
[api, derivePath, isDevelopment, isValid, name, onClose, onStatusChange, pairType, password, seed, t]
|
||||
);
|
||||
|
||||
return (
|
||||
@ -247,144 +247,166 @@ function Create ({ className = '', onClose, onStatusChange, seed: propsSeed, typ
|
||||
header={t<string>('Add an account via seed')}
|
||||
size='large'
|
||||
>
|
||||
{address && isConfirmationOpen && (
|
||||
<CreateConfirmation
|
||||
address={address}
|
||||
isBusy={isBusy}
|
||||
name={name}
|
||||
onClose={toggleConfirmation}
|
||||
onCommit={_onCommit}
|
||||
/>
|
||||
)}
|
||||
<Modal.Content>
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<AddressRow
|
||||
defaultName={name}
|
||||
noDefaultNameOpacity
|
||||
value={isSeedValid ? address : ''}
|
||||
/>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<Input
|
||||
autoFocus
|
||||
help={t<string>('Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g "name_you_want - stash".')}
|
||||
isError={!isNameValid}
|
||||
label={t<string>('name')}
|
||||
onChange={_onChangeName}
|
||||
{address && isConfirmationOpen
|
||||
? (
|
||||
<CreateConfirmation
|
||||
address={address}
|
||||
derivePath={derivePath}
|
||||
isBusy={isBusy}
|
||||
name={name}
|
||||
pairType={pairType}
|
||||
seed={seed}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<Modal.Content>
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<AddressRow
|
||||
defaultName={name}
|
||||
noDefaultNameOpacity
|
||||
value={isSeedValid ? address : ''}
|
||||
/>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<Input
|
||||
autoFocus
|
||||
help={t<string>('Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g "name_you_want - stash".')}
|
||||
isError={!isNameValid}
|
||||
label={t<string>('name')}
|
||||
onChange={_onChangeName}
|
||||
onEnter={_onCommit}
|
||||
placeholder={t<string>('new account')}
|
||||
value={name}
|
||||
/>
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('The name for this account and how it will appear under your addresses. With an on-chain identity, it can be made available to others.')}</p>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<Input
|
||||
help={t<string>('The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the "--key" parameter of your node.')}
|
||||
isAction
|
||||
isError={!isSeedValid}
|
||||
isReadOnly={seedType === 'dev'}
|
||||
label={
|
||||
seedType === 'bip'
|
||||
? t<string>('mnemonic seed')
|
||||
: seedType === 'dev'
|
||||
? t<string>('development seed')
|
||||
: t<string>('seed (hex or string)')
|
||||
}
|
||||
onChange={_onChangeSeed}
|
||||
onEnter={_onCommit}
|
||||
value={seed}
|
||||
>
|
||||
<Dropdown
|
||||
defaultValue={seedType}
|
||||
isButton
|
||||
onChange={_selectSeedType}
|
||||
options={seedOpt}
|
||||
/>
|
||||
<CopyButton
|
||||
className='copyMoved'
|
||||
value={seed}
|
||||
/>
|
||||
</Input>
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('The secret seed value for this account. Ensure that you keep this in a safe place, with access to the seed you can re-create the account.')}</p>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
<PasswordInput
|
||||
onChange={_onPasswordChange}
|
||||
onEnter={_onCommit}
|
||||
placeholder={t<string>('new account')}
|
||||
value={name}
|
||||
/>
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('The name for this account and how it will appear under your addresses. With an on-chain identity, it can be made available to others.')}</p>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<Input
|
||||
help={t<string>('The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the "--key" parameter of your node.')}
|
||||
isAction
|
||||
isError={!isSeedValid}
|
||||
isReadOnly={seedType === 'dev'}
|
||||
label={
|
||||
seedType === 'bip'
|
||||
? t<string>('mnemonic seed')
|
||||
: seedType === 'dev'
|
||||
? t<string>('development seed')
|
||||
: t<string>('seed (hex or string)')
|
||||
}
|
||||
onChange={_onChangeSeed}
|
||||
onEnter={_onCommit}
|
||||
value={seed}
|
||||
{!isElectron && (
|
||||
<article className='warning'>
|
||||
<p>{t<string>('Consider storing your account in a signer such as a browser extension, hardware device, QR-capable phone wallet (non-connected) or desktop application for optimal account security.')} {t<string>('Future versions of the web-only interface will drop support for non-external accounts, much like the IPFS version.')}</p>
|
||||
</article>
|
||||
)}
|
||||
<Expander
|
||||
className='accounts--Creator-advanced'
|
||||
isPadded
|
||||
summary={t<string>('Advanced creation options')}
|
||||
>
|
||||
<Dropdown
|
||||
defaultValue={seedType}
|
||||
isButton
|
||||
onChange={_selectSeedType}
|
||||
options={seedOpt}
|
||||
/>
|
||||
<CopyButton
|
||||
className='copyMoved'
|
||||
value={seed}
|
||||
/>
|
||||
</Input>
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('The secret seed value for this account. Ensure that you keep this in a safe place, with access to the seed you can re-create the account.')}</p>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
<PasswordInput
|
||||
onChange={_onPasswordChange}
|
||||
onEnter={_onCommit}
|
||||
password={password}
|
||||
/>
|
||||
{!isElectron && (
|
||||
<article className='warning'>
|
||||
<p>{t<string>('Consider storing your account in a signer such as a browser extension, hardware device, QR-capable phone wallet (non-connected) or desktop application for optimal account security.')} {t<string>('Future versions of the web-only interface will drop support for non-external accounts, much like the IPFS version.')}</p>
|
||||
</article>
|
||||
)}
|
||||
<Expander
|
||||
className='accounts--Creator-advanced'
|
||||
isPadded
|
||||
summary={t<string>('Advanced creation options')}
|
||||
>
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<Dropdown
|
||||
defaultValue={pairType}
|
||||
help={t<string>('Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use "ed25519".')}
|
||||
label={t<string>('keypair crypto type')}
|
||||
onChange={_onChangePairType}
|
||||
options={uiSettings.availableCryptos}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('If you are moving accounts between applications, ensure that you use the correct type.')}</p>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<Input
|
||||
help={t<string>('You can set a custom derivation path for this account using the following syntax "/<soft-key>//<hard-key>". The "/<soft-key>" and "//<hard-key>" may be repeated and mixed`. An optional "///<password>" can be used with a mnemonic seed, and may only be specified once.')}
|
||||
isError={!!deriveError}
|
||||
label={t<string>('secret derivation path')}
|
||||
onChange={_onChangeDerive}
|
||||
onEnter={_onCommit}
|
||||
placeholder={
|
||||
seedType === 'raw'
|
||||
? pairType === 'sr25519'
|
||||
? t<string>('//hard/soft')
|
||||
: t<string>('//hard')
|
||||
: pairType === 'sr25519'
|
||||
? t<string>('//hard/soft///password')
|
||||
: t<string>('//hard///password')
|
||||
}
|
||||
tabIndex={-1}
|
||||
value={derivePath}
|
||||
/>
|
||||
{deriveError && (
|
||||
<article className='error'>{deriveError}</article>
|
||||
)}
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('The derivation path allows you to create different accounts from the same base mnemonic.')}</p>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
</Expander>
|
||||
</Modal.Content>
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<Dropdown
|
||||
defaultValue={pairType}
|
||||
help={t<string>('Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use "ed25519".')}
|
||||
label={t<string>('keypair crypto type')}
|
||||
onChange={_onChangePairType}
|
||||
options={uiSettings.availableCryptos}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('If you are moving accounts between applications, ensure that you use the correct type.')}</p>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<Input
|
||||
help={t<string>('You can set a custom derivation path for this account using the following syntax "/<soft-key>//<hard-key>". The "/<soft-key>" and "//<hard-key>" may be repeated and mixed`. An optional "///<password>" can be used with a mnemonic seed, and may only be specified once.')}
|
||||
isError={!!deriveError}
|
||||
label={t<string>('secret derivation path')}
|
||||
onChange={_onChangeDerive}
|
||||
onEnter={_onCommit}
|
||||
placeholder={
|
||||
seedType === 'raw'
|
||||
? pairType === 'sr25519'
|
||||
? t<string>('//hard/soft')
|
||||
: t<string>('//hard')
|
||||
: pairType === 'sr25519'
|
||||
? t<string>('//hard/soft///password')
|
||||
: t<string>('//hard///password')
|
||||
}
|
||||
tabIndex={-1}
|
||||
value={derivePath}
|
||||
/>
|
||||
{deriveError && (
|
||||
<article className='error'>{deriveError}</article>
|
||||
)}
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('The derivation path allows you to create different accounts from the same base mnemonic.')}</p>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
</Expander>
|
||||
</Modal.Content>
|
||||
)
|
||||
}
|
||||
<Modal.Actions onCancel={onClose}>
|
||||
<Button
|
||||
icon='plus'
|
||||
isBusy={isBusy}
|
||||
isDisabled={!isValid}
|
||||
label={t<string>('Save')}
|
||||
onClick={toggleConfirmation}
|
||||
/>
|
||||
{isConfirmationOpen
|
||||
? (
|
||||
<>
|
||||
<Button
|
||||
icon='step-backward'
|
||||
label={t<string>('Prev')}
|
||||
onClick={toggleConfirmation}
|
||||
/>
|
||||
<Button
|
||||
icon='plus'
|
||||
isBusy={isBusy}
|
||||
label={t<string>('Save')}
|
||||
onClick={_onCommit}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
icon='step-forward'
|
||||
isDisabled={!isValid}
|
||||
label={t<string>('Next')}
|
||||
onClick={toggleConfirmation}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@ -2,44 +2,62 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
import React from 'react';
|
||||
import { AddressRow, Button, Modal } from '@polkadot/react-components';
|
||||
import { AddressRow, Modal, Static } from '@polkadot/react-components';
|
||||
import { isHex } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
address: string;
|
||||
derivePath: string;
|
||||
isBusy: boolean;
|
||||
name: string;
|
||||
onClose: () => void;
|
||||
onCommit: () => void;
|
||||
pairType: KeypairType;
|
||||
seed?: string;
|
||||
}
|
||||
|
||||
function CreateConfirmation ({ address, isBusy, name, onClose, onCommit }: Props): React.ReactElement<Props> | null {
|
||||
function CreateConfirmation ({ address, derivePath, name, pairType, seed }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const splitSeed = seed && seed.split(' ');
|
||||
const shortSeed = isHex(seed)
|
||||
? `${seed.substr(10)} … ${seed.substr(-8)}`
|
||||
: splitSeed && splitSeed.map((value, index) => (index % 3) ? '…' : value).join(' ');
|
||||
|
||||
return (
|
||||
<Modal header={t<string>('Important notice')}>
|
||||
<Modal.Content>
|
||||
<AddressRow
|
||||
defaultName={name}
|
||||
isInline
|
||||
noDefaultNameOpacity
|
||||
value={address}
|
||||
>
|
||||
<Modal.Content>
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<AddressRow
|
||||
defaultName={name}
|
||||
isInline
|
||||
noDefaultNameOpacity
|
||||
value={address}
|
||||
/>
|
||||
{shortSeed && (
|
||||
<Static
|
||||
label={t<string>('partial seed')}
|
||||
value={shortSeed}
|
||||
/>
|
||||
)}
|
||||
<Static
|
||||
label={t<string>('keypair type')}
|
||||
value={pairType}
|
||||
/>
|
||||
<Static
|
||||
label={t<string>('derivation path')}
|
||||
value={derivePath || t<string>('<none provided>')}
|
||||
/>
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('We will provide you with a generated backup file after your account is created. As long as you have access to your account you can always download this file later by clicking on "Backup" button from the Accounts section.')}</p>
|
||||
<p>{t<string>('Please make sure to save this file in a secure location as it is required, together with your password, to restore your account.')}</p>
|
||||
</AddressRow>
|
||||
</Modal.Content>
|
||||
<Modal.Actions onCancel={onClose}>
|
||||
<Button
|
||||
icon='plus'
|
||||
isBusy={isBusy}
|
||||
label={t<string>('Create and backup account')}
|
||||
onClick={onCommit}
|
||||
/>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
</Modal.Content>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -160,13 +160,12 @@ function Derive ({ className = '', from, onClose }: Props): React.ReactElement {
|
||||
setTimeout((): void => {
|
||||
const status = createAccount(source, suri, name, password, t<string>('created account'), isDevelopment ? undefined : api.genesisHash.toString());
|
||||
|
||||
toggleConfirmation();
|
||||
queueAction(status);
|
||||
setIsBusy(false);
|
||||
onClose();
|
||||
}, 0);
|
||||
},
|
||||
[api, isDevelopment, isValid, name, onClose, password, queueAction, source, suri, t, toggleConfirmation]
|
||||
[api, isDevelopment, isValid, name, onClose, password, queueAction, source, suri, t]
|
||||
);
|
||||
|
||||
const sourceStatic = (
|
||||
@ -183,74 +182,78 @@ function Derive ({ className = '', from, onClose }: Props): React.ReactElement {
|
||||
className={className}
|
||||
header={t<string>('Derive account from pair')}
|
||||
>
|
||||
{address && isConfirmationOpen && (
|
||||
<CreateConfirmation
|
||||
address={address}
|
||||
isBusy={isBusy}
|
||||
name={name}
|
||||
onClose={toggleConfirmation}
|
||||
onCommit={_onCommit}
|
||||
/>
|
||||
)}
|
||||
<Modal.Content>
|
||||
{isLocked && (
|
||||
<>
|
||||
{sourceStatic}
|
||||
<Password
|
||||
autoFocus
|
||||
help={t<string>('The password to unlock the selected account.')}
|
||||
isError={!!lockedError}
|
||||
label={t<string>('password')}
|
||||
onChange={_onChangeRootPass}
|
||||
value={rootPass}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{!isLocked && (
|
||||
<AddressRow
|
||||
defaultName={name}
|
||||
noDefaultNameOpacity
|
||||
value={deriveError ? '' : address}
|
||||
>
|
||||
{sourceStatic}
|
||||
<Input
|
||||
autoFocus
|
||||
help={t<string>('You can set a custom derivation path for this account using the following syntax "/<soft-key>//<hard-key>///<password>". The "/<soft-key>" and "//<hard-key>" may be repeated and mixed`. The "///password" is optional and should only occur once.')}
|
||||
label={t<string>('derivation path')}
|
||||
onChange={setSuri}
|
||||
placeholder={t<string>('//hard/soft')}
|
||||
/>
|
||||
<Input
|
||||
className='full'
|
||||
help={t<string>('Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g "name_you_want - stash".')}
|
||||
isError={!isNameValid}
|
||||
label={t<string>('name')}
|
||||
onChange={_onChangeName}
|
||||
onEnter={_onCommit}
|
||||
placeholder={t<string>('new account')}
|
||||
value={name}
|
||||
/>
|
||||
<Password
|
||||
className='full'
|
||||
help={t<string>('This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).')}
|
||||
isError={!isPassValid}
|
||||
label={t<string>('password')}
|
||||
onChange={_onChangePass}
|
||||
onEnter={_onCommit}
|
||||
value={password}
|
||||
/>
|
||||
<Password
|
||||
className='full'
|
||||
help={t<string>('Verify the password entered above.')}
|
||||
isError={!isPass2Valid}
|
||||
label={t<string>('password (repeat)')}
|
||||
onChange={_onChangePass2}
|
||||
onEnter={_onCommit}
|
||||
value={password2}
|
||||
/>
|
||||
</AddressRow>
|
||||
)}
|
||||
</Modal.Content>
|
||||
{address && isConfirmationOpen
|
||||
? (
|
||||
<CreateConfirmation
|
||||
address={address}
|
||||
derivePath={suri}
|
||||
isBusy={isBusy}
|
||||
name={name}
|
||||
pairType={source.type}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<Modal.Content>
|
||||
{isLocked && (
|
||||
<>
|
||||
{sourceStatic}
|
||||
<Password
|
||||
autoFocus
|
||||
help={t<string>('The password to unlock the selected account.')}
|
||||
isError={!!lockedError}
|
||||
label={t<string>('password')}
|
||||
onChange={_onChangeRootPass}
|
||||
value={rootPass}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{!isLocked && (
|
||||
<AddressRow
|
||||
defaultName={name}
|
||||
noDefaultNameOpacity
|
||||
value={deriveError ? '' : address}
|
||||
>
|
||||
{sourceStatic}
|
||||
<Input
|
||||
autoFocus
|
||||
help={t<string>('You can set a custom derivation path for this account using the following syntax "/<soft-key>//<hard-key>///<password>". The "/<soft-key>" and "//<hard-key>" may be repeated and mixed`. The "///password" is optional and should only occur once.')}
|
||||
label={t<string>('derivation path')}
|
||||
onChange={setSuri}
|
||||
placeholder={t<string>('//hard/soft')}
|
||||
/>
|
||||
<Input
|
||||
className='full'
|
||||
help={t<string>('Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g "name_you_want - stash".')}
|
||||
isError={!isNameValid}
|
||||
label={t<string>('name')}
|
||||
onChange={_onChangeName}
|
||||
onEnter={_onCommit}
|
||||
placeholder={t<string>('new account')}
|
||||
value={name}
|
||||
/>
|
||||
<Password
|
||||
className='full'
|
||||
help={t<string>('This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).')}
|
||||
isError={!isPassValid}
|
||||
label={t<string>('password')}
|
||||
onChange={_onChangePass}
|
||||
onEnter={_onCommit}
|
||||
value={password}
|
||||
/>
|
||||
<Password
|
||||
className='full'
|
||||
help={t<string>('Verify the password entered above.')}
|
||||
isError={!isPass2Valid}
|
||||
label={t<string>('password (repeat)')}
|
||||
onChange={_onChangePass2}
|
||||
onEnter={_onCommit}
|
||||
value={password2}
|
||||
/>
|
||||
</AddressRow>
|
||||
)}
|
||||
</Modal.Content>
|
||||
)
|
||||
}
|
||||
<Modal.Actions onCancel={onClose}>
|
||||
{isLocked
|
||||
? (
|
||||
@ -262,14 +265,30 @@ function Derive ({ className = '', from, onClose }: Props): React.ReactElement {
|
||||
onClick={_onUnlock}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
icon='plus'
|
||||
isBusy={isBusy}
|
||||
isDisabled={!isValid}
|
||||
label={t<string>('Save')}
|
||||
onClick={toggleConfirmation}
|
||||
/>
|
||||
: (isConfirmationOpen
|
||||
? (
|
||||
<>
|
||||
<Button
|
||||
icon='step-backward'
|
||||
label={t<string>('Prev')}
|
||||
onClick={toggleConfirmation}
|
||||
/>
|
||||
<Button
|
||||
icon='plus'
|
||||
isBusy={isBusy}
|
||||
label={t<string>('Save')}
|
||||
onClick={_onCommit}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
icon='step-forward'
|
||||
isDisabled={!isValid}
|
||||
label={t<string>('Next')}
|
||||
onClick={toggleConfirmation}
|
||||
/>
|
||||
)
|
||||
)
|
||||
}
|
||||
</Modal.Actions>
|
||||
|
||||
@ -2,47 +2,41 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Modal, Password, PasswordStrength } from '@polkadot/react-components';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
type Props = {
|
||||
password: string;
|
||||
onChange: (password: string, isPasswordValid: boolean) => void;
|
||||
onEnter: () => void;
|
||||
}
|
||||
|
||||
export default function PasswordInput ({ onChange, onEnter, password }: Props): React.ReactElement {
|
||||
export default function PasswordInput ({ onChange, onEnter }: Props): React.ReactElement {
|
||||
const { t } = useTranslation();
|
||||
const [isPassValid, setPassValid] = useState<boolean>(false);
|
||||
const [{ isPass1Valid, password1 }, setPassword1] = useState({ isPass1Valid: false, password1: '' });
|
||||
const [{ isPass2Valid, password2 }, setPassword2] = useState({ isPass2Valid: false, password2: '' });
|
||||
|
||||
const _onPasswordChange = useCallback(
|
||||
(password: string) => {
|
||||
const isPassValid = keyring.isPassValid(password);
|
||||
useEffect(
|
||||
() => onChange(password1, isPass1Valid && isPass2Valid),
|
||||
[password1, onChange, isPass1Valid, isPass2Valid]
|
||||
);
|
||||
|
||||
setPassValid(isPassValid);
|
||||
|
||||
const isValid = isPassValid && isPass2Valid;
|
||||
|
||||
onChange(password, isValid);
|
||||
},
|
||||
[onChange, isPass2Valid]
|
||||
const _onPassword1Change = useCallback(
|
||||
(password1: string) => setPassword1({
|
||||
isPass1Valid: keyring.isPassValid(password1),
|
||||
password1
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const onPassword2Change = useCallback(
|
||||
(password2: string) => {
|
||||
const isPass2Valid = keyring.isPassValid(password2) && (password2 === password);
|
||||
|
||||
setPassword2({ isPass2Valid, password2 });
|
||||
|
||||
const isValid = isPassValid && isPass2Valid;
|
||||
|
||||
onChange(password, isValid);
|
||||
},
|
||||
[password, onChange, isPassValid]
|
||||
(password2: string) => setPassword2({
|
||||
isPass2Valid: keyring.isPassValid(password2) && (password2 === password1),
|
||||
password2
|
||||
}),
|
||||
[password1]
|
||||
);
|
||||
|
||||
return (
|
||||
@ -51,11 +45,11 @@ export default function PasswordInput ({ onChange, onEnter, password }: Props):
|
||||
<Password
|
||||
className='full'
|
||||
help={t<string>('This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).')}
|
||||
isError={!isPassValid}
|
||||
isError={!isPass1Valid}
|
||||
label={t<string>('password')}
|
||||
onChange={_onPasswordChange}
|
||||
onChange={_onPassword1Change}
|
||||
onEnter={onEnter}
|
||||
value={password}
|
||||
value={password1}
|
||||
/>
|
||||
<Password
|
||||
className='full'
|
||||
@ -66,7 +60,7 @@ export default function PasswordInput ({ onChange, onEnter, password }: Props):
|
||||
onEnter={onEnter}
|
||||
value={password2}
|
||||
/>
|
||||
<PasswordStrength value={password} />
|
||||
<PasswordStrength value={password1} />
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('The password and password confirmation for this account. This is required to authenticate any transactions made and to encrypt the keypair.')}</p>
|
||||
@ -11,8 +11,8 @@ import { AddressRow, Button, Input, InputAddress, Modal, QrScanAddress } from '@
|
||||
import { useApi, useIpfs } from '@polkadot/react-hooks';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
|
||||
import PasswordInput from '../Accounts/PasswordInput';
|
||||
import { useTranslation } from '../translate';
|
||||
import PasswordInput from './PasswordInput';
|
||||
|
||||
interface Scanned {
|
||||
content: string;
|
||||
@ -41,7 +41,6 @@ function QrModal ({ className = '', onClose, onStatusChange }: Props): React.Rea
|
||||
const [{ address, isAddress, scanned }, setAddress] = useState<Address>({ address: '', isAddress: false, scanned: null });
|
||||
const [{ isPasswordValid, password }, setPassword] = useState({ isPasswordValid: false, password: '' });
|
||||
|
||||
// FIXME This gets passed to the useCallback, so we have excessive creations
|
||||
const isValid = !!address && isNameValid && (isAddress || isPasswordValid);
|
||||
|
||||
const _onNameChange = useCallback(
|
||||
@ -139,7 +138,6 @@ function QrModal ({ className = '', onClose, onStatusChange }: Props): React.Rea
|
||||
<PasswordInput
|
||||
onChange={_onPasswordChange}
|
||||
onEnter={_onSave}
|
||||
password={password}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user