Forget confirmation & account name editing (#4)
* Forget confirmation & account name editing * Update ActionBar padding
This commit is contained in:
parent
7f35ea1e8e
commit
93b135d685
@ -6,7 +6,8 @@
|
||||
"license": "Apache-2",
|
||||
"dependencies": {
|
||||
"@polkadot/ui-identicon": "^0.39.0-beta.5",
|
||||
"@types/react-router-dom": "^4.3.2",
|
||||
"@types/react-router": "^5.0.0",
|
||||
"@types/react-router-dom": "^4.3.3",
|
||||
"react-router": "^5.0.0",
|
||||
"react-router-dom": "^5.0.0"
|
||||
}
|
||||
|
||||
@ -2,31 +2,58 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { ActionBar, Address } from '../../components';
|
||||
import { forgetAccount } from '../../messaging';
|
||||
import { editAccount } from '../../messaging';
|
||||
import { Name } from '../../partials';
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
address: string,
|
||||
name?: string,
|
||||
name?: string | null,
|
||||
onAction: () => void
|
||||
};
|
||||
|
||||
export default function Account ({ address, name, onAction }: Props) {
|
||||
const _onForget = (): void => {
|
||||
forgetAccount(address)
|
||||
.then(onAction)
|
||||
.catch(console.error);
|
||||
export default function Account ({ address, className, name, onAction }: Props) {
|
||||
const [isEditing, setEditing] = useState(false);
|
||||
const [editedname, setName] = useState(name);
|
||||
|
||||
const _toggleEdit = (): void => {
|
||||
setEditing(!isEditing);
|
||||
};
|
||||
const _saveChanges = (): void => {
|
||||
if (editedname && editedname !== name) {
|
||||
editAccount(address, editedname)
|
||||
.then(onAction)
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
_toggleEdit();
|
||||
};
|
||||
|
||||
return (
|
||||
<Address
|
||||
address={address}
|
||||
name={name}
|
||||
className={className}
|
||||
name={
|
||||
isEditing
|
||||
? (
|
||||
<Name
|
||||
defaultValue={name}
|
||||
isFocussed
|
||||
label={null}
|
||||
onBlur={_saveChanges}
|
||||
onChange={setName}
|
||||
/>
|
||||
)
|
||||
: name
|
||||
}
|
||||
>
|
||||
<ActionBar>
|
||||
<a onClick={_onForget}>Forget</a>
|
||||
<a onClick={_toggleEdit}>Edit</a>
|
||||
<Link to={`/account/forget/${address}`}>Forget</Link>
|
||||
</ActionBar>
|
||||
</Address>
|
||||
);
|
||||
|
||||
47
packages/extension-ui/src/Popup/Forget.tsx
Normal file
47
packages/extension-ui/src/Popup/Forget.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2019 @polkadot/extension-ui authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { KeyringJson } from '@polkadot/ui-keyring/types';
|
||||
|
||||
import React from 'react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
|
||||
import { Address, Button, Header, Tip } from '../components';
|
||||
import { forgetAccount } from '../messaging';
|
||||
import { Back } from '../partials';
|
||||
|
||||
type Props = RouteComponentProps<{ address: string }> & {
|
||||
accounts: Array<KeyringJson>,
|
||||
onAction: () => void
|
||||
};
|
||||
|
||||
function Forget (props: Props) {
|
||||
const { accounts, match: { params: { address } }, onAction } = props;
|
||||
const account = accounts.find((account) => address === account.address);
|
||||
|
||||
const _onClick = (): void => {
|
||||
forgetAccount(address)
|
||||
.then(onAction)
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header label='forget account' />
|
||||
<Back />
|
||||
<Tip header='danger' type='error'>You are about to remove the account. This means that you will not be able to access it via this extension anymore. If you wish to recover it, you would need to use the seed.</Tip>
|
||||
<Address
|
||||
address={address}
|
||||
name={account && account.meta.name}
|
||||
/>
|
||||
<Button
|
||||
isFull
|
||||
label='I want to forget this account'
|
||||
onClick={_onClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default withRouter(Forget);
|
||||
@ -34,7 +34,7 @@ export default function Request ({ accounts, isFirst, onAction, request: { addre
|
||||
return (
|
||||
<Address
|
||||
address={address}
|
||||
name={account ? account.meta.name : '<invalid account>'}
|
||||
name={account && account.meta.name}
|
||||
>
|
||||
<Details
|
||||
method={method}
|
||||
|
||||
@ -12,6 +12,7 @@ import { Loading } from '../components';
|
||||
import { getAccounts, getRequests } from '../messaging';
|
||||
import Accounts from './Accounts';
|
||||
import Create from './Create';
|
||||
import Forget from './Forget';
|
||||
import Import from './Import';
|
||||
import Signing from './Signing';
|
||||
|
||||
@ -43,6 +44,9 @@ export default function Popup (props: Props) {
|
||||
<Route path='/account/create' component={() =>
|
||||
<Create onAction={onAction} />
|
||||
} />
|
||||
<Route path='/account/forget/:address' component={() =>
|
||||
<Forget accounts={accounts} onAction={onAction} />
|
||||
} />
|
||||
<Route path='/account/import' component={() =>
|
||||
<Import onAction={onAction} />
|
||||
} />
|
||||
|
||||
@ -5,8 +5,6 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import defaults from './defaults';
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
@ -21,7 +19,7 @@ function ActionBar ({ children, className }: Props) {
|
||||
}
|
||||
|
||||
export default styled(ActionBar)`
|
||||
padding: ${defaults.boxPadding};
|
||||
padding: 0.25rem;
|
||||
text-align: right;
|
||||
|
||||
a {
|
||||
|
||||
@ -14,7 +14,7 @@ type Props = {
|
||||
children?: React.ReactNode;
|
||||
className?: string,
|
||||
isHidden?: boolean,
|
||||
name?: string | null,
|
||||
name?: React.ReactNode | null,
|
||||
theme?: 'polkadot' | 'substrate'
|
||||
};
|
||||
|
||||
|
||||
@ -10,32 +10,44 @@ import defaults from './defaults';
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
defaultValue?: string | null,
|
||||
isError?: boolean,
|
||||
isFocussed?: boolean,
|
||||
isReadOnly?: boolean,
|
||||
label: string,
|
||||
label?: string | null,
|
||||
onBlur?: () => void,
|
||||
onChange?: (value: string) => void,
|
||||
type?: 'text' | 'password',
|
||||
value?: string
|
||||
};
|
||||
|
||||
function Input ({ className, label, isFocussed, isReadOnly, onChange, type = 'text', value }: Props) {
|
||||
function Input ({ className, defaultValue, label, isFocussed, isReadOnly, onBlur, onChange, type = 'text', value }: Props) {
|
||||
const _onChange = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
onChange && onChange(value.trim());
|
||||
};
|
||||
|
||||
const element = (
|
||||
<input
|
||||
autoFocus={isFocussed}
|
||||
defaultValue={defaultValue || undefined}
|
||||
readOnly={isReadOnly}
|
||||
onBlur={onBlur}
|
||||
onChange={_onChange}
|
||||
type={type}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
|
||||
if (!label) {
|
||||
return <div className={className}>{element}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Label
|
||||
className={className}
|
||||
label={label}
|
||||
>
|
||||
<input
|
||||
autoFocus={isFocussed}
|
||||
readOnly={isReadOnly}
|
||||
onChange={_onChange}
|
||||
type={type}
|
||||
value={value}
|
||||
/>
|
||||
{element}
|
||||
</Label>
|
||||
);
|
||||
}
|
||||
|
||||
@ -22,6 +22,10 @@ function sendMessage (message: MessageTypes, request: any = {}): Promise<any> {
|
||||
});
|
||||
}
|
||||
|
||||
export async function editAccount (address: string, name: string): Promise<boolean> {
|
||||
return sendMessage('accounts.edit', { address, name });
|
||||
}
|
||||
|
||||
export async function forgetAccount (address: string): Promise<boolean> {
|
||||
return sendMessage('accounts.forget', { address });
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ type Props = {
|
||||
function Back ({ className, to = '/' }: Props) {
|
||||
return (
|
||||
<div className={className}>
|
||||
<Link to='/'>{unicode.BACK} Back</Link>
|
||||
<Link to={to}>{unicode.BACK} Back</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -7,14 +7,18 @@ import React, { useEffect, useState } from 'react';
|
||||
import { Input } from '../components';
|
||||
|
||||
type Props = {
|
||||
defaultValue?: string | null,
|
||||
isFocussed?: boolean,
|
||||
onChange: (name: string | null) => void
|
||||
label?: string | null,
|
||||
onBlur?: () => void,
|
||||
onChange: (name: string | null) => void,
|
||||
value?: string
|
||||
};
|
||||
|
||||
const MIN_LENGTH = 3;
|
||||
|
||||
export default function Name ({ isFocussed, onChange }: Props) {
|
||||
const [name, setName] = useState('');
|
||||
export default function Name ({ defaultValue, isFocussed, label = 'a descriptive name for this account', onBlur, onChange, value }: Props) {
|
||||
const [name, setName] = useState(defaultValue || '');
|
||||
|
||||
useEffect(() => {
|
||||
onChange(
|
||||
@ -25,14 +29,15 @@ export default function Name ({ isFocussed, onChange }: Props) {
|
||||
}, [name]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
isError={name.length < MIN_LENGTH}
|
||||
isFocussed={isFocussed}
|
||||
label='a descriptive name for this account'
|
||||
onChange={setName}
|
||||
type='text'
|
||||
/>
|
||||
</>
|
||||
<Input
|
||||
defaultValue={defaultValue}
|
||||
isError={name.length < MIN_LENGTH}
|
||||
isFocussed={isFocussed}
|
||||
label={label}
|
||||
onBlur={onBlur}
|
||||
onChange={setName}
|
||||
type='text'
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { KeyringJson } from '@polkadot/ui-keyring/types';
|
||||
import { MessageTypes, MessageAccountCreate, MessageExtrinsicSign, MessageExtrinsicSignApprove, MessageExtrinsicSignCancel, MessageSeedCreate, MessageSeedCreate$Response, MessageSeedValidate, MessageSeedValidate$Response, MessageAccountForget } from '../types';
|
||||
import { MessageTypes, MessageAccountCreate, MessageAccountEdit, MessageExtrinsicSign, MessageExtrinsicSignApprove, MessageExtrinsicSignCancel, MessageSeedCreate, MessageSeedCreate$Response, MessageSeedValidate, MessageSeedValidate$Response, MessageAccountForget } from '../types';
|
||||
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import accountsObservable from '@polkadot/ui-keyring/observable/accounts';
|
||||
@ -29,6 +29,16 @@ export default class Extension {
|
||||
return true;
|
||||
}
|
||||
|
||||
private accountsEdit ({ address, name }: MessageAccountEdit): boolean {
|
||||
const pair = keyring.getPair(address);
|
||||
|
||||
assert(pair, 'Unable to find pair');
|
||||
|
||||
keyring.saveAccountMeta(pair, { ...pair.getMeta(), name });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private accountsForget ({ address }: MessageAccountForget): boolean {
|
||||
keyring.forgetAccount(address);
|
||||
|
||||
@ -113,6 +123,9 @@ export default class Extension {
|
||||
case 'accounts.forget':
|
||||
return this.accountsForget(request);
|
||||
|
||||
case 'accounts.edit':
|
||||
return this.accountsEdit(request);
|
||||
|
||||
case 'accounts.list':
|
||||
return this.accountsList();
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
export type MessageTypes = 'accounts.create' | 'accounts.forget' | 'accounts.list' | 'extrinsic.sign' | 'seed.create' | 'seed.validate' | 'signing.approve' | 'signing.cancel' | 'signing.requests';
|
||||
export type MessageTypes = 'accounts.create' | 'accounts.edit' | 'accounts.forget' | 'accounts.list' | 'extrinsic.sign' | 'seed.create' | 'seed.validate' | 'signing.approve' | 'signing.cancel' | 'signing.requests';
|
||||
|
||||
export type MessageRequest = {
|
||||
id: number,
|
||||
@ -25,6 +25,11 @@ export type MessageAccountCreate = {
|
||||
type?: KeypairType
|
||||
};
|
||||
|
||||
export type MessageAccountEdit= {
|
||||
address: string,
|
||||
name: string
|
||||
};
|
||||
|
||||
export type MessageAccountForget = {
|
||||
address: string
|
||||
};
|
||||
|
||||
@ -2591,7 +2591,7 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-router-dom@^4.3.2":
|
||||
"@types/react-router-dom@^4.3.3":
|
||||
version "4.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.3.tgz#7837e3e9fefbc84a8f6c8a51dca004f4e83e94e3"
|
||||
integrity sha512-xj0DmFjgvAqRfh/kJPO7apD5G30yPQe+8slu/dugioQOkdKpyzc4Fgk4hoTelm6CSHz7pI2PPsW5+Y6GRBF2zw==
|
||||
@ -2600,7 +2600,7 @@
|
||||
"@types/react" "*"
|
||||
"@types/react-router" "*"
|
||||
|
||||
"@types/react-router@*":
|
||||
"@types/react-router@*", "@types/react-router@^5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.0.0.tgz#22ae8f55d8af770ea1f755218936f01bfe1bfe27"
|
||||
integrity sha512-0JjtJMxkQSyWUHTHaD3GhKf6rcZSUFmcQob8OlPTsbnxnIg2Nh3btkss4uke5CKVRtbCMipGU7My5jtfZQC+jw==
|
||||
|
||||
Loading…
Reference in New Issue
Block a user