From 93b135d685f28ddd6418fa330c8f57712dc39cdb Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 20 May 2019 15:56:11 +0200 Subject: [PATCH] Forget confirmation & account name editing (#4) * Forget confirmation & account name editing * Update ActionBar padding --- packages/extension-ui/package.json | 3 +- .../src/Popup/Accounts/Account.tsx | 47 +++++++++++++++---- packages/extension-ui/src/Popup/Forget.tsx | 47 +++++++++++++++++++ .../src/Popup/Signing/Request.tsx | 2 +- packages/extension-ui/src/Popup/index.tsx | 4 ++ .../extension-ui/src/components/ActionBar.tsx | 4 +- .../extension-ui/src/components/Address.tsx | 2 +- .../extension-ui/src/components/Input.tsx | 30 ++++++++---- packages/extension-ui/src/messaging.ts | 4 ++ packages/extension-ui/src/partials/Back.tsx | 2 +- packages/extension-ui/src/partials/Name.tsx | 29 +++++++----- .../src/background/handlers/Extension.ts | 15 +++++- packages/extension/src/background/types.ts | 7 ++- yarn.lock | 4 +- 14 files changed, 158 insertions(+), 42 deletions(-) create mode 100644 packages/extension-ui/src/Popup/Forget.tsx diff --git a/packages/extension-ui/package.json b/packages/extension-ui/package.json index 7483580c..7ea603a1 100644 --- a/packages/extension-ui/package.json +++ b/packages/extension-ui/package.json @@ -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" } diff --git a/packages/extension-ui/src/Popup/Accounts/Account.tsx b/packages/extension-ui/src/Popup/Accounts/Account.tsx index cb37634a..c7d4c0da 100644 --- a/packages/extension-ui/src/Popup/Accounts/Account.tsx +++ b/packages/extension-ui/src/Popup/Accounts/Account.tsx @@ -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 (
+ ) + : name + } > - Forget + Edit + Forget
); diff --git a/packages/extension-ui/src/Popup/Forget.tsx b/packages/extension-ui/src/Popup/Forget.tsx new file mode 100644 index 00000000..e4ffe21d --- /dev/null +++ b/packages/extension-ui/src/Popup/Forget.tsx @@ -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, + 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 ( +
+
+ + 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. +
+
+ ); +} + +export default withRouter(Forget); diff --git a/packages/extension-ui/src/Popup/Signing/Request.tsx b/packages/extension-ui/src/Popup/Signing/Request.tsx index c297a1b2..e906893a 100644 --- a/packages/extension-ui/src/Popup/Signing/Request.tsx +++ b/packages/extension-ui/src/Popup/Signing/Request.tsx @@ -34,7 +34,7 @@ export default function Request ({ accounts, isFirst, onAction, request: { addre return (
'} + name={account && account.meta.name} >
} /> + + + } /> } /> diff --git a/packages/extension-ui/src/components/ActionBar.tsx b/packages/extension-ui/src/components/ActionBar.tsx index cfa043cc..cdc60f04 100644 --- a/packages/extension-ui/src/components/ActionBar.tsx +++ b/packages/extension-ui/src/components/ActionBar.tsx @@ -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 { diff --git a/packages/extension-ui/src/components/Address.tsx b/packages/extension-ui/src/components/Address.tsx index 50057ed6..284c516f 100644 --- a/packages/extension-ui/src/components/Address.tsx +++ b/packages/extension-ui/src/components/Address.tsx @@ -14,7 +14,7 @@ type Props = { children?: React.ReactNode; className?: string, isHidden?: boolean, - name?: string | null, + name?: React.ReactNode | null, theme?: 'polkadot' | 'substrate' }; diff --git a/packages/extension-ui/src/components/Input.tsx b/packages/extension-ui/src/components/Input.tsx index 3f797236..0b0a7495 100644 --- a/packages/extension-ui/src/components/Input.tsx +++ b/packages/extension-ui/src/components/Input.tsx @@ -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): void => { onChange && onChange(value.trim()); }; + const element = ( + + ); + + if (!label) { + return
{element}
; + } + return ( ); } diff --git a/packages/extension-ui/src/messaging.ts b/packages/extension-ui/src/messaging.ts index 36d8077b..da7bdd85 100644 --- a/packages/extension-ui/src/messaging.ts +++ b/packages/extension-ui/src/messaging.ts @@ -22,6 +22,10 @@ function sendMessage (message: MessageTypes, request: any = {}): Promise { }); } +export async function editAccount (address: string, name: string): Promise { + return sendMessage('accounts.edit', { address, name }); +} + export async function forgetAccount (address: string): Promise { return sendMessage('accounts.forget', { address }); } diff --git a/packages/extension-ui/src/partials/Back.tsx b/packages/extension-ui/src/partials/Back.tsx index ec743fc3..0010eb39 100644 --- a/packages/extension-ui/src/partials/Back.tsx +++ b/packages/extension-ui/src/partials/Back.tsx @@ -16,7 +16,7 @@ type Props = { function Back ({ className, to = '/' }: Props) { return (
- {unicode.BACK} Back + {unicode.BACK} Back
); } diff --git a/packages/extension-ui/src/partials/Name.tsx b/packages/extension-ui/src/partials/Name.tsx index bd0f7534..17c4a429 100644 --- a/packages/extension-ui/src/partials/Name.tsx +++ b/packages/extension-ui/src/partials/Name.tsx @@ -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 ( - <> - - + ); } diff --git a/packages/extension/src/background/handlers/Extension.ts b/packages/extension/src/background/handlers/Extension.ts index db2e40a1..2a65e990 100644 --- a/packages/extension/src/background/handlers/Extension.ts +++ b/packages/extension/src/background/handlers/Extension.ts @@ -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(); diff --git a/packages/extension/src/background/types.ts b/packages/extension/src/background/types.ts index 4c9f3c19..120e0f49 100644 --- a/packages/extension/src/background/types.ts +++ b/packages/extension/src/background/types.ts @@ -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 }; diff --git a/yarn.lock b/yarn.lock index 3b8479fb..98026bdb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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==