Display accounts first-and-foremost on all creations (#426)
* Display accounts first-and-foremost on all creations * Fix tests
This commit is contained in:
parent
04d0b2700f
commit
ae5d076de9
@ -14,6 +14,7 @@ Changes:
|
|||||||
- Derived accounts uses the parent genesisHash by default (attaches to same chain)
|
- Derived accounts uses the parent genesisHash by default (attaches to same chain)
|
||||||
- Make import from seed, QR & JSON options available on first-start
|
- Make import from seed, QR & JSON options available on first-start
|
||||||
- Adjust popup width, allowing full display of e.g. addresses
|
- Adjust popup width, allowing full display of e.g. addresses
|
||||||
|
- Always display network selection on all accounts
|
||||||
- Handling signing rejections (any order) transparently
|
- Handling signing rejections (any order) transparently
|
||||||
- Small overall UI and use adjustments
|
- Small overall UI and use adjustments
|
||||||
- Latest upstream polkadot-js dependencies
|
- Latest upstream polkadot-js dependencies
|
||||||
|
|||||||
@ -65,7 +65,7 @@ const WINDOW_OPTS = {
|
|||||||
top: 150,
|
top: 150,
|
||||||
type: 'popup',
|
type: 'popup',
|
||||||
url: chrome.extension.getURL('index.html'),
|
url: chrome.extension.getURL('index.html'),
|
||||||
width: 480
|
width: 560
|
||||||
};
|
};
|
||||||
|
|
||||||
function getId (): string {
|
function getId (): string {
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
import { RequestAuthorizeTab } from '@polkadot/extension-base/background/types';
|
import { RequestAuthorizeTab } from '@polkadot/extension-base/background/types';
|
||||||
import { ThemeProps } from '../../types';
|
import { ThemeProps } from '../../types';
|
||||||
|
|
||||||
import React, { useContext } from 'react';
|
import React, { useCallback, useContext } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { ActionBar, ActionContext, Button, Icon, Link, Warning } from '../../components';
|
import { ActionBar, ActionContext, Button, Icon, Link, Warning } from '../../components';
|
||||||
@ -21,14 +21,20 @@ interface Props {
|
|||||||
|
|
||||||
function Request ({ authId, className, isFirst, request: { origin }, url }: Props): React.ReactElement<Props> {
|
function Request ({ authId, className, isFirst, request: { origin }, url }: Props): React.ReactElement<Props> {
|
||||||
const onAction = useContext(ActionContext);
|
const onAction = useContext(ActionContext);
|
||||||
const _onApprove = (): Promise<void> =>
|
|
||||||
approveAuthRequest(authId)
|
const _onApprove = useCallback(
|
||||||
|
() => approveAuthRequest(authId)
|
||||||
.then(() => onAction())
|
.then(() => onAction())
|
||||||
.catch((error: Error) => console.error(error));
|
.catch((error: Error) => console.error(error)),
|
||||||
const _onReject = (): Promise<void> =>
|
[authId, onAction]
|
||||||
rejectAuthRequest(authId)
|
);
|
||||||
|
|
||||||
|
const _onReject = useCallback(
|
||||||
|
() => rejectAuthRequest(authId)
|
||||||
.then(() => onAction())
|
.then(() => onAction())
|
||||||
.catch((error: Error) => console.error(error));
|
.catch((error: Error) => console.error(error)),
|
||||||
|
[authId, onAction]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { Address, BackButton, ButtonArea, NextStepButton, VerticalSpace } from '../../components';
|
import { BackButton, ButtonArea, NextStepButton, VerticalSpace } from '../../components';
|
||||||
import { Name, Password } from '../../partials';
|
import { Name, Password } from '../../partials';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -14,7 +14,7 @@ interface Props {
|
|||||||
onCreate: (name: string, password: string) => void | Promise<void | boolean>;
|
onCreate: (name: string, password: string) => void | Promise<void | boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AccountName ({ address, isBusy, onBackClick, onCreate }: Props): React.ReactElement<Props> {
|
function AccountName ({ isBusy, onBackClick, onCreate }: Props): React.ReactElement<Props> {
|
||||||
const [name, setName] = useState<string | null>(null);
|
const [name, setName] = useState<string | null>(null);
|
||||||
const [password, setPassword] = useState<string | null>(null);
|
const [password, setPassword] = useState<string | null>(null);
|
||||||
|
|
||||||
@ -29,13 +29,7 @@ function AccountName ({ address, isBusy, onBackClick, onCreate }: Props): React.
|
|||||||
isFocused
|
isFocused
|
||||||
onChange={setName}
|
onChange={setName}
|
||||||
/>
|
/>
|
||||||
{name && <Password onChange={setPassword} />}
|
<Password onChange={setPassword} />
|
||||||
{name && password && (
|
|
||||||
<Address
|
|
||||||
address={address}
|
|
||||||
name={name}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<VerticalSpace />
|
<VerticalSpace />
|
||||||
<ButtonArea>
|
<ButtonArea>
|
||||||
<BackButton onClick={onBackClick} />
|
<BackButton onClick={onBackClick} />
|
||||||
@ -52,4 +46,4 @@ function AccountName ({ address, isBusy, onBackClick, onCreate }: Props): React.
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AccountName;
|
export default React.memo(AccountName);
|
||||||
|
|||||||
@ -42,7 +42,7 @@ describe('Create Account', () => {
|
|||||||
wrapper.update();
|
wrapper.update();
|
||||||
};
|
};
|
||||||
|
|
||||||
const enterName = (name: string): Promise<void> => type(wrapper.find('input'), name);
|
const enterName = (name: string): Promise<void> => type(wrapper.find('input').first(), name);
|
||||||
const password = (password: string) => (): Promise<void> => type(wrapper.find('input[type="password"]').first(), password);
|
const password = (password: string) => (): Promise<void> => type(wrapper.find('input[type="password"]').first(), password);
|
||||||
const repeat = (password: string) => (): Promise<void> => type(wrapper.find('input[type="password"]').last(), password);
|
const repeat = (password: string) => (): Promise<void> => type(wrapper.find('input[type="password"]').last(), password);
|
||||||
|
|
||||||
@ -89,20 +89,20 @@ describe('Create Account', () => {
|
|||||||
|
|
||||||
it('only account name input is visible at first', () => {
|
it('only account name input is visible at first', () => {
|
||||||
expect(wrapper.find(InputWithLabel).find('[data-input-name]').find(Input)).toHaveLength(1);
|
expect(wrapper.find(InputWithLabel).find('[data-input-name]').find(Input)).toHaveLength(1);
|
||||||
expect(wrapper.find(InputWithLabel).find('[data-input-password]')).toHaveLength(0);
|
expect(wrapper.find(InputWithLabel).find('[data-input-password]').find(Input)).toHaveLength(1);
|
||||||
expect(wrapper.find(InputWithLabel).find('[data-input-repeat-password]')).toHaveLength(0);
|
expect(wrapper.find(InputWithLabel).find('[data-input-repeat-password]')).toHaveLength(0);
|
||||||
expect(wrapper.find('[data-button-action="add new root"] button').prop('disabled')).toBe(true);
|
expect(wrapper.find('[data-button-action="add new root"] button').prop('disabled')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('input should not be highlighted as error until first interaction', () => {
|
it('input should not be highlighted as error until first interaction', () => {
|
||||||
expect(wrapper.find(Input).prop('withError')).toBe(false);
|
expect(wrapper.find(InputWithLabel).find('[data-input-name]').find(Input).prop('withError')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('after typing less than 3 characters into name input, password input is not visible', async () => {
|
it('after typing less than 3 characters into name input, password input is not visible', async () => {
|
||||||
await enterName('ab');
|
await enterName('ab');
|
||||||
expect(wrapper.find(Input).prop('withError')).toBe(true);
|
expect(wrapper.find(InputWithLabel).find('[data-input-name]').find(Input).prop('withError')).toBe(true);
|
||||||
expect(wrapper.find('ErrorMessage').text()).toBe('Account name is too short');
|
expect(wrapper.find('ErrorMessage').first().text()).toBe('Account name is too short');
|
||||||
expect(wrapper.find(InputWithLabel).find('[data-input-password]')).toHaveLength(0);
|
expect(wrapper.find(InputWithLabel).find('[data-input-password]').find(Input)).toHaveLength(1);
|
||||||
expect(wrapper.find(InputWithLabel).find('[data-input-repeat-password]')).toHaveLength(0);
|
expect(wrapper.find(InputWithLabel).find('[data-input-repeat-password]')).toHaveLength(0);
|
||||||
expect(wrapper.find('[data-button-action="add new root"] button').prop('disabled')).toBe(true);
|
expect(wrapper.find('[data-button-action="add new root"] button').prop('disabled')).toBe(true);
|
||||||
});
|
});
|
||||||
@ -110,12 +110,12 @@ describe('Create Account', () => {
|
|||||||
it('input should keep showing error when something has been typed but then erased', async () => {
|
it('input should keep showing error when something has been typed but then erased', async () => {
|
||||||
await enterName('ab');
|
await enterName('ab');
|
||||||
await enterName('');
|
await enterName('');
|
||||||
expect(wrapper.find(Input).prop('withError')).toBe(true);
|
expect(wrapper.find(InputWithLabel).find('[data-input-name]').find(Input).prop('withError')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('after typing 3 characters into name input, first password input is visible', async () => {
|
it('after typing 3 characters into name input, first password input is visible', async () => {
|
||||||
await enterName('abc');
|
await enterName('abc');
|
||||||
expect(wrapper.find(Input).first().prop('withError')).toBe(false);
|
expect(wrapper.find(InputWithLabel).find('[data-input-name]').find(Input).first().prop('withError')).toBe(false);
|
||||||
expect(wrapper.find(InputWithLabel).find('[data-input-password]').find(Input)).toHaveLength(1);
|
expect(wrapper.find(InputWithLabel).find('[data-input-password]').find(Input)).toHaveLength(1);
|
||||||
expect(wrapper.find(InputWithLabel).find('[data-input-repeat-password]')).toHaveLength(0);
|
expect(wrapper.find(InputWithLabel).find('[data-input-repeat-password]')).toHaveLength(0);
|
||||||
expect(wrapper.find('[data-button-action="add new root"] button').prop('disabled')).toBe(true);
|
expect(wrapper.find('[data-button-action="add new root"] button').prop('disabled')).toBe(true);
|
||||||
|
|||||||
@ -33,12 +33,11 @@ function Mnemonic ({ onNextStep, seed }: Props): React.ReactElement<Props> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Warning>Please write down your wallet’s mnemonic seed and keep it in a safe place. <br />
|
|
||||||
Mnemonic seed is used to restore your wallet. Keep it carefully in case you lose your assets.</Warning>
|
|
||||||
<MnemonicSeed
|
<MnemonicSeed
|
||||||
onCopy={_onCopy}
|
onCopy={_onCopy}
|
||||||
seed={seed}
|
seed={seed}
|
||||||
/>
|
/>
|
||||||
|
<Warning>Please write down your wallet’s mnemonic seed and keep it in a safe place. The mnemonic can be used to restore your wallet. Keep it carefully to not lose your assets.</Warning>
|
||||||
<VerticalSpace />
|
<VerticalSpace />
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isMnemonicSaved}
|
checked={isMnemonicSaved}
|
||||||
@ -57,4 +56,4 @@ function Mnemonic ({ onNextStep, seed }: Props): React.ReactElement<Props> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Mnemonic;
|
export default React.memo(Mnemonic);
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||||
import { ActionContext, Loading } from '../../components';
|
import { ActionContext, Address, Loading } from '../../components';
|
||||||
import { createAccountSuri, createSeed } from '../../messaging';
|
import { createAccountSuri, createSeed } from '../../messaging';
|
||||||
import { HeaderWithSteps } from '../../partials';
|
import { HeaderWithSteps } from '../../partials';
|
||||||
import AccountName from './AccountName';
|
import AccountName from './AccountName';
|
||||||
@ -27,6 +27,7 @@ export default function CreateAccount (): React.ReactElement {
|
|||||||
// this should always be the case
|
// this should always be the case
|
||||||
if (name && password && account) {
|
if (name && password && account) {
|
||||||
setIsBusy(true);
|
setIsBusy(true);
|
||||||
|
|
||||||
createAccountSuri(name, password, account.seed)
|
createAccountSuri(name, password, account.seed)
|
||||||
.then(() => onAction('/'))
|
.then(() => onAction('/'))
|
||||||
.catch((error: Error): void => {
|
.catch((error: Error): void => {
|
||||||
@ -47,23 +48,31 @@ export default function CreateAccount (): React.ReactElement {
|
|||||||
step={step}
|
step={step}
|
||||||
text='Create an account: '
|
text='Create an account: '
|
||||||
/>
|
/>
|
||||||
<Loading>{account && (
|
<Loading>
|
||||||
step === 1
|
<div>
|
||||||
? (
|
<Address
|
||||||
<Mnemonic
|
address={account?.address}
|
||||||
onNextStep={_onNextStep}
|
name={name}
|
||||||
seed={account.seed}
|
/>
|
||||||
/>
|
</div>
|
||||||
)
|
{account && (
|
||||||
: (
|
step === 1
|
||||||
<AccountName
|
? (
|
||||||
address={account.address}
|
<Mnemonic
|
||||||
isBusy={isBusy}
|
onNextStep={_onNextStep}
|
||||||
onBackClick={_onPreviousStep}
|
seed={account.seed}
|
||||||
onCreate={_onCreate}
|
/>
|
||||||
/>
|
)
|
||||||
)
|
: (
|
||||||
)}</Loading>
|
<AccountName
|
||||||
|
address={account.address}
|
||||||
|
isBusy={isBusy}
|
||||||
|
onBackClick={_onPreviousStep}
|
||||||
|
onCreate={_onCreate}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Loading>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,13 +12,14 @@ import { Address, Svg } from '../../components';
|
|||||||
import useOutsideClick from '../../hooks/useOutsideClick';
|
import useOutsideClick from '../../hooks/useOutsideClick';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
allAddresses: string[];
|
allAddresses: [string, string | null][];
|
||||||
className?: string;
|
className?: string;
|
||||||
onSelect: (address: string) => void;
|
onSelect: (address: string) => void;
|
||||||
selectedAddress: string;
|
selectedAddress: string;
|
||||||
|
selectedGenesis: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AddressDropdown ({ allAddresses, className, onSelect, selectedAddress }: Props): React.ReactElement<Props> {
|
function AddressDropdown ({ allAddresses, className, onSelect, selectedAddress, selectedGenesis }: Props): React.ReactElement<Props> {
|
||||||
const [isDropdownVisible, setDropdownVisible] = useState(false);
|
const [isDropdownVisible, setDropdownVisible] = useState(false);
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -34,16 +35,22 @@ function AddressDropdown ({ allAddresses, className, onSelect, selectedAddress }
|
|||||||
onClick={_toggleDropdown}
|
onClick={_toggleDropdown}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
<Address address={selectedAddress} />
|
<Address
|
||||||
|
address={selectedAddress}
|
||||||
|
genesisHash={selectedGenesis}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<HiddenOptions visible={isDropdownVisible}>
|
<HiddenOptions visible={isDropdownVisible}>
|
||||||
{allAddresses.map((address) => (
|
{allAddresses.map(([address, genesisHash]) => (
|
||||||
<div
|
<div
|
||||||
data-parent-option
|
data-parent-option
|
||||||
key={address}
|
key={address}
|
||||||
onClick={_selectParent(address)}
|
onClick={_selectParent(address)}
|
||||||
>
|
>
|
||||||
<Address address={address} />
|
<Address
|
||||||
|
address={address}
|
||||||
|
genesisHash={genesisHash}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</HiddenOptions>
|
</HiddenOptions>
|
||||||
@ -54,7 +61,7 @@ function AddressDropdown ({ allAddresses, className, onSelect, selectedAddress }
|
|||||||
const HiddenOptions = styled.div<{ visible: boolean }>`
|
const HiddenOptions = styled.div<{ visible: boolean }>`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
visibility: ${({ visible }): string => (visible ? 'visible' : 'hidden')};
|
visibility: ${({ visible }): string => (visible ? 'visible' : 'hidden')};
|
||||||
width: 430px;
|
width: 510px;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
background: ${({ theme }: ThemeProps): string => theme.bodyColor};
|
background: ${({ theme }: ThemeProps): string => theme.bodyColor};
|
||||||
max-height: ${({ visible }): string => (visible ? '200px' : '0')};
|
max-height: ${({ visible }): string => (visible ? '200px' : '0')};
|
||||||
@ -77,7 +84,7 @@ export default styled(AddressDropdown)(({ theme }: ThemeProps) => `
|
|||||||
& > div:first-child > ${Address}::after {
|
& > div:first-child > ${Address}::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 66%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
right: 11px;
|
right: 11px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { assert } from '@polkadot/util';
|
import { assert } from '@polkadot/util';
|
||||||
|
|
||||||
@ -15,10 +15,11 @@ import DerivationPath from './DerivationPath';
|
|||||||
interface Props {
|
interface Props {
|
||||||
isLocked?: boolean;
|
isLocked?: boolean;
|
||||||
parentAddress: string;
|
parentAddress: string;
|
||||||
|
parentGenesis: string | null;
|
||||||
onDerivationConfirmed: (derivation: { account: { address: string; suri: string }; parentPassword: string }) => void;
|
onDerivationConfirmed: (derivation: { account: { address: string; suri: string }; parentPassword: string }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress }: Props): React.ReactElement<Props> {
|
export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress, parentGenesis }: Props): React.ReactElement<Props> {
|
||||||
const onAction = useContext(ActionContext);
|
const onAction = useContext(ActionContext);
|
||||||
const [isBusy, setIsBusy] = useState(false);
|
const [isBusy, setIsBusy] = useState(false);
|
||||||
const { accounts, hierarchy } = useContext(AccountContext);
|
const { accounts, hierarchy } = useContext(AccountContext);
|
||||||
@ -30,6 +31,13 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress }
|
|||||||
|
|
||||||
const passwordInputRef = useRef<HTMLDivElement>(null);
|
const passwordInputRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const allAddresses = useMemo(
|
||||||
|
() => hierarchy
|
||||||
|
.filter(({ isExternal }) => !isExternal)
|
||||||
|
.map(({ address, genesisHash }): [string, string | null] => [address, genesisHash || null]),
|
||||||
|
[hierarchy]
|
||||||
|
);
|
||||||
|
|
||||||
const _goCreate = useCallback(
|
const _goCreate = useCallback(
|
||||||
() => onAction('/account/create'),
|
() => onAction('/account/create'),
|
||||||
[onAction]
|
[onAction]
|
||||||
@ -91,13 +99,19 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress }
|
|||||||
)}
|
)}
|
||||||
<DisableableArea isDisabled={!shouldAccountBeDerived}>
|
<DisableableArea isDisabled={!shouldAccountBeDerived}>
|
||||||
{isLocked
|
{isLocked
|
||||||
? <Address address={parentAddress} />
|
? (
|
||||||
|
<Address
|
||||||
|
address={parentAddress}
|
||||||
|
genesisHash={parentGenesis}
|
||||||
|
/>
|
||||||
|
)
|
||||||
: (
|
: (
|
||||||
<Label label='Choose Parent Account:'>
|
<Label label='Choose Parent Account:'>
|
||||||
<AddressDropdown
|
<AddressDropdown
|
||||||
allAddresses={hierarchy.filter(({ isExternal }) => !isExternal).map(({ address }) => address)}
|
allAddresses={allAddresses}
|
||||||
onSelect={_onParentChange}
|
onSelect={_onParentChange}
|
||||||
selectedAddress={parentAddress}
|
selectedAddress={parentAddress}
|
||||||
|
selectedGenesis={parentGenesis}
|
||||||
/>
|
/>
|
||||||
</Label>
|
</Label>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import React, { useCallback, useContext, useState } from 'react';
|
import React, { useCallback, useContext, useMemo, useState } from 'react';
|
||||||
import { useParams } from 'react-router';
|
import { useParams } from 'react-router';
|
||||||
|
|
||||||
import { AccountContext, ActionContext, Address, BackButton, ButtonArea, NextStepButton, VerticalSpace } from '../../components';
|
import { AccountContext, ActionContext, Address, BackButton, ButtonArea, NextStepButton, VerticalSpace } from '../../components';
|
||||||
@ -37,22 +37,24 @@ function Derive ({ isLocked }: Props): React.ReactElement<Props> {
|
|||||||
const [password, setPassword] = useState<string | null>(null);
|
const [password, setPassword] = useState<string | null>(null);
|
||||||
const [parentPassword, setParentPassword] = useState<string | null>(null);
|
const [parentPassword, setParentPassword] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const parentGenesis = useMemo(
|
||||||
|
() => accounts.find((a) => a.address === parentAddress)?.genesisHash || null,
|
||||||
|
[accounts, parentAddress]
|
||||||
|
);
|
||||||
|
|
||||||
const _onCreate = useCallback(() => {
|
const _onCreate = useCallback(() => {
|
||||||
if (!account || !name || !password || !parentPassword) {
|
if (!account || !name || !password || !parentPassword) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentAccount = accounts.find((a) => a.address === parentAddress);
|
|
||||||
const genesisHash = parentAccount?.genesisHash || null;
|
|
||||||
|
|
||||||
setIsBusy(true);
|
setIsBusy(true);
|
||||||
deriveAccount(parentAddress, account.suri, parentPassword, name, password, genesisHash)
|
deriveAccount(parentAddress, account.suri, parentPassword, name, password, parentGenesis)
|
||||||
.then(() => onAction('/'))
|
.then(() => onAction('/'))
|
||||||
.catch((error): void => {
|
.catch((error): void => {
|
||||||
setIsBusy(false);
|
setIsBusy(false);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
}, [account, accounts, name, password, onAction, parentAddress, parentPassword]);
|
}, [account, name, password, onAction, parentAddress, parentGenesis, parentPassword]);
|
||||||
|
|
||||||
const _onDerivationConfirmed = useCallback(({ account, parentPassword }: ConfirmState) => {
|
const _onDerivationConfirmed = useCallback(({ account, parentPassword }: ConfirmState) => {
|
||||||
setAccount(account);
|
setAccount(account);
|
||||||
@ -74,23 +76,23 @@ function Derive ({ isLocked }: Props): React.ReactElement<Props> {
|
|||||||
isLocked={isLocked}
|
isLocked={isLocked}
|
||||||
onDerivationConfirmed={_onDerivationConfirmed}
|
onDerivationConfirmed={_onDerivationConfirmed}
|
||||||
parentAddress={parentAddress}
|
parentAddress={parentAddress}
|
||||||
/>
|
parentGenesis={parentGenesis}
|
||||||
)}
|
|
||||||
{account && (
|
|
||||||
<Name
|
|
||||||
isFocused
|
|
||||||
onChange={setName}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{account && name && <Password onChange={setPassword}/>}
|
|
||||||
{account && name && password && (
|
|
||||||
<Address
|
|
||||||
address={account.address}
|
|
||||||
name={name}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{account && (
|
{account && (
|
||||||
<>
|
<>
|
||||||
|
<div>
|
||||||
|
<Address
|
||||||
|
address={account.address}
|
||||||
|
genesisHash={parentGenesis}
|
||||||
|
name={name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Name
|
||||||
|
isFocused
|
||||||
|
onChange={setName}
|
||||||
|
/>
|
||||||
|
<Password onChange={setPassword} />
|
||||||
<VerticalSpace/>
|
<VerticalSpace/>
|
||||||
<ButtonArea>
|
<ButtonArea>
|
||||||
<BackButton onClick={_onBackClick}/>
|
<BackButton onClick={_onBackClick}/>
|
||||||
@ -108,4 +110,4 @@ function Derive ({ isLocked }: Props): React.ReactElement<Props> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Derive;
|
export default React.memo(Derive);
|
||||||
|
|||||||
@ -36,14 +36,16 @@ export default function ImportQr (): React.ReactElement {
|
|||||||
)}
|
)}
|
||||||
{account && (
|
{account && (
|
||||||
<>
|
<>
|
||||||
|
<div>
|
||||||
|
<Address
|
||||||
|
{...account}
|
||||||
|
name={name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<Name
|
<Name
|
||||||
isFocused
|
isFocused
|
||||||
onChange={setName}
|
onChange={setName}
|
||||||
/>
|
/>
|
||||||
<Address
|
|
||||||
{...account}
|
|
||||||
name={name}
|
|
||||||
/>
|
|
||||||
<VerticalSpace />
|
<VerticalSpace />
|
||||||
{name && (
|
{name && (
|
||||||
<ButtonArea>
|
<ButtonArea>
|
||||||
|
|||||||
@ -40,10 +40,14 @@ export default function Import (): React.ReactElement {
|
|||||||
const _onChangeSeed = useCallback(
|
const _onChangeSeed = useCallback(
|
||||||
async (suri: string | null): Promise<void> => {
|
async (suri: string | null): Promise<void> => {
|
||||||
if (suri) {
|
if (suri) {
|
||||||
setAccount(await validateSeed(suri));
|
try {
|
||||||
} else {
|
return setAccount(await validateSeed(suri));
|
||||||
setAccount(null);
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setAccount(null);
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
@ -69,6 +73,12 @@ export default function Import (): React.ReactElement {
|
|||||||
showBackArrow
|
showBackArrow
|
||||||
text='Import account'
|
text='Import account'
|
||||||
/>
|
/>
|
||||||
|
<div>
|
||||||
|
<Address
|
||||||
|
address={account?.address}
|
||||||
|
name={name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<ValidatedInput
|
<ValidatedInput
|
||||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||||
component={SeedInput}
|
component={SeedInput}
|
||||||
@ -78,14 +88,14 @@ export default function Import (): React.ReactElement {
|
|||||||
rowsCount={2}
|
rowsCount={2}
|
||||||
validator={isSeedValid}
|
validator={isSeedValid}
|
||||||
/>
|
/>
|
||||||
{account && <Name onChange={setName} />}
|
{account && (
|
||||||
{account && name && <Password onChange={setPassword} />}
|
<>
|
||||||
|
<Name onChange={setName} />
|
||||||
|
<Password onChange={setPassword} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{account && name && password && (
|
{account && name && password && (
|
||||||
<>
|
<>
|
||||||
<Address
|
|
||||||
address={account.address}
|
|
||||||
name={name}
|
|
||||||
/>
|
|
||||||
<VerticalSpace />
|
<VerticalSpace />
|
||||||
<ButtonArea>
|
<ButtonArea>
|
||||||
<NextStepButton
|
<NextStepButton
|
||||||
|
|||||||
@ -96,10 +96,13 @@ export default function Upload (): React.ReactElement {
|
|||||||
text='Restore from JSON'
|
text='Restore from JSON'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<Address
|
<div>
|
||||||
address={(isFileValid && address) || null}
|
<Address
|
||||||
name={(isFileValid && json?.meta.name as string) || null}
|
address={(isFileValid && address) || null}
|
||||||
/>
|
genesisHash={(isFileValid && json?.meta.genesisHash as string) || null}
|
||||||
|
name={(isFileValid && json?.meta.name as string) || null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<InputFileWithLabel
|
<InputFileWithLabel
|
||||||
accept={acceptedFormats}
|
accept={acceptedFormats}
|
||||||
isError={!isFileValid}
|
isError={!isFileValid}
|
||||||
|
|||||||
@ -48,6 +48,7 @@ export default function Request ({ account: { isExternal }, buttonText, isFirst,
|
|||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
setIsLocked(null);
|
setIsLocked(null);
|
||||||
|
|
||||||
!isExternal && isSignLocked(signId)
|
!isExternal && isSignLocked(signId)
|
||||||
.then((isLocked) => setIsLocked(isLocked))
|
.then((isLocked) => setIsLocked(isLocked))
|
||||||
.catch((error: Error) => console.error(error));
|
.catch((error: Error) => console.error(error));
|
||||||
|
|||||||
@ -40,7 +40,7 @@ function MnemonicSeed ({ className, onCopy, seed }: Props): React.ReactElement<P
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default styled(MnemonicSeed)(({ theme }: ThemeProps) => `
|
export default styled(MnemonicSeed)(({ theme }: ThemeProps) => `
|
||||||
margin-top: 21px;
|
margin-bottom: 21px;
|
||||||
|
|
||||||
.buttonsRow {
|
.buttonsRow {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<title>polkadot{.js}</title>
|
<title>polkadot{.js}</title>
|
||||||
</head>
|
</head>
|
||||||
<body style="height: 600px; margin:0; overflow-x:hidden; text-align:center">
|
<body style="height: 600px; margin:0; overflow-x:hidden; text-align:center">
|
||||||
<div id="root" style="box-sizing:border-box; margin:0 auto; padding:0; text-align:left; width:570px; height: calc(100% - 2px); max-width: 100%;"></div>
|
<div id="root" style="box-sizing:border-box; margin:0 auto; padding:0; text-align:left; width:560px; height: calc(100% - 2px); max-width: 100%;"></div>
|
||||||
<script src='./extension.js'></script>
|
<script src='./extension.js'></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user