Rework account & address modals (#1143)

* Modal styling

* Update account forgetting

* Swap name prop to AddressRow/Summary to defaultName

* typo

* Backup modal

* Don't allow edit on isTesting accounts

* ChangePassword modal update

* Label help on password labels

* typo

* Create & backup dialog

* Addressbook forget

* Update packages/app-accounts/src/modals/Forgetting.tsx

Co-Authored-By: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>

* Update packages/app-accounts/src/Creator.tsx

Co-Authored-By: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>

* Update packages/app-accounts/src/modals/Backup.tsx

Co-Authored-By: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>

* Update packages/app-accounts/src/modals/Backup.tsx

Co-Authored-By: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>

* Update packages/app-address-book/src/Forgetting.tsx

Co-Authored-By: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>

* Update packages/app-address-book/src/Forgetting.tsx

Co-Authored-By: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>

* Update packages/app-accounts/src/modals/ChangePass.tsx

Co-Authored-By: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>

* Update packages/app-accounts/src/modals/Backup.tsx

Co-Authored-By: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>

* Update packages/app-accounts/src/modals/Forgetting.tsx

Co-Authored-By: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>
This commit is contained in:
Jaco Greeff 2019-05-13 15:59:04 +02:00 committed by GitHub
parent cc242962a0
commit 904b07005a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 163 additions and 137 deletions

View File

@ -12,7 +12,7 @@ import FileSaver from 'file-saver';
import React from 'react';
import { DEV_PHRASE } from '@polkadot/keyring/defaults';
import { withApi, withMulti } from '@polkadot/ui-api';
import { AddressSummary, Button, Dropdown, Input, Labelled, Modal, Password } from '@polkadot/ui-app';
import { AddressRow, AddressSummary, Button, Dropdown, Input, Labelled, Modal, Password } from '@polkadot/ui-app';
import { InputAddress } from '@polkadot/ui-app/InputAddress';
import keyring from '@polkadot/ui-keyring';
import uiSettings from '@polkadot/ui-settings';
@ -156,7 +156,7 @@ class Creator extends React.PureComponent<Props, State> {
private renderInput () {
const { t } = this.props;
const { deriveError, derivePath, isNameValid, isPassValid, isSeedValid, name, pairType, password, seed, seedOptions, seedType, showWarning } = this.state;
const { deriveError, derivePath, isNameValid, isPassValid, isSeedValid, name, pairType, password, seed, seedOptions, seedType } = this.state;
const seedLabel = (() => {
switch (seedType) {
case 'bip':
@ -241,61 +241,50 @@ class Creator extends React.PureComponent<Props, State> {
}
</div>
</details>
<Modal
className='app--accounts-Modal'
dimmer='inverted'
open={showWarning}
size='small'
>
{this.renderModalContent()}
{this.renderModalButtons()}
</Modal>
{this.renderModal()}
</div>
);
}
private renderModalButtons () {
private renderModal () {
const { t } = this.props;
const { address, name, showWarning } = this.state;
return (
<Modal.Actions>
<Button.Group>
<Button
isNegative
label={t('Cancel')}
onClick={this.onHideWarning}
/>
<Button.Or />
<Button
isPrimary
label={t('Create and backup account')}
onClick={this.onCommit}
/>
</Button.Group>
</Modal.Actions>
);
}
private renderModalContent () {
const { t } = this.props;
const { address } = this.state;
return (
<>
<Modal
className='app--accounts-Modal'
dimmer='inverted'
open={showWarning}
>
<Modal.Header>
{t('Important notice!')}
{t('Important notice')}
</Modal.Header>
<Modal.Content>
{t('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 redownload this file later.')}
<Modal.Description>
{t('Please make sure to save this file in a secure location as it is required, together with your password, to restore your account.')}
</Modal.Description>
<AddressSummary
className='accounts--Modal-Address'
<AddressRow
defaultName={name}
isInline
value={address}
/>
>
<p>{t('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('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>
<Button.Group>
<Button
isNegative
label={t('Cancel')}
onClick={this.onHideWarning}
/>
<Button.Or />
<Button
isPrimary
label={t('Create and backup account')}
onClick={this.onCommit}
/>
</Button.Group>
</Modal.Actions>
</Modal>
);
}

View File

@ -13,9 +13,9 @@ import { AddressSummary, Button, Dropdown, Input, InputAddress } from '@polkadot
import keyring from '@polkadot/ui-keyring';
import uiSettings from '@polkadot/ui-settings';
import Backup from './Backup';
import ChangePass from './ChangePass';
import Forgetting from './Forgetting';
import Backup from './modals/Backup';
import ChangePass from './modals/ChangePass';
import Forgetting from './modals/Forgetting';
import translate from './translate';
type Props = ComponentProps & I18nProps & {
@ -54,7 +54,7 @@ class Editor extends React.PureComponent<Props, State> {
const { t } = this.props;
const { current, isEdited } = this.state;
if (!current) {
if (!current || current.getMeta().isTesting) {
return null;
}

View File

@ -7,11 +7,11 @@ import { I18nProps } from '@polkadot/ui-app/types';
import FileSaver from 'file-saver';
import React from 'react';
import { AddressSummary, Button, Modal, Password } from '@polkadot/ui-app';
import { AddressRow, Button, Modal, Password } from '@polkadot/ui-app';
import { ActionStatus } from '@polkadot/ui-app/Status/types';
import keyring from '@polkadot/ui-keyring';
import translate from './translate';
import translate from '../translate';
type Props = I18nProps & {
onStatusChange: (status: ActionStatus) => void,
@ -36,7 +36,6 @@ class Backup extends React.PureComponent<Props, State> {
className='app--accounts-Modal'
dimmer='inverted'
open
size='tiny'
>
{this.renderContent()}
{this.renderButtons()}
@ -44,7 +43,7 @@ class Backup extends React.PureComponent<Props, State> {
);
}
renderButtons () {
private renderButtons () {
const { onClose, t } = this.props;
const { isPassValid } = this.state;
@ -68,7 +67,7 @@ class Backup extends React.PureComponent<Props, State> {
);
}
renderContent () {
private renderContent () {
const { pair, t } = this.props;
const { isPassValid, password } = this.state;
@ -78,14 +77,23 @@ class Backup extends React.PureComponent<Props, State> {
{t('Backup account')}
</Modal.Header>
<Modal.Content className='app--account-Backup-content'>
<AddressSummary value={pair.address()} />
<Password
isError={!isPassValid}
label={t('unlock account using the password')}
onChange={this.onChangePass}
tabIndex={0}
value={password}
/>
<AddressRow
isInline
value={pair.address()}
>
<p>{t('An encrypted backup file will be created once you have pressed the "Download" button. This can be used to re-import your account on any other machine.')}</p>
<p>{t('Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.')}</p>
<div>
<Password
help={t('The account password as specified when creating the account. This is used to encrypt the backup file and subsequently decrypt it when restoring the account.')}
isError={!isPassValid}
label={t('password')}
onChange={this.onChangePass}
tabIndex={0}
value={password}
/>
</div>
</AddressRow>
</Modal.Content>
</>
);

View File

@ -6,11 +6,11 @@ import { KeyringPair } from '@polkadot/keyring/types';
import { I18nProps } from '@polkadot/ui-app/types';
import React from 'react';
import { AddressSummary, Button, Modal, Password } from '@polkadot/ui-app';
import { AddressRow, Button, Modal, Password } from '@polkadot/ui-app';
import { ActionStatus } from '@polkadot/ui-app/Status/types';
import keyring from '@polkadot/ui-keyring';
import translate from './translate';
import translate from '../translate';
type Props = I18nProps & {
account: KeyringPair,
@ -39,7 +39,6 @@ class ChangePass extends React.PureComponent<Props, State> {
className='app--accounts-Modal'
dimmer='inverted'
open
size='tiny'
>
{this.renderContent()}
{this.renderButtons()}
@ -81,22 +80,31 @@ class ChangePass extends React.PureComponent<Props, State> {
{t('Change account password')}
</Modal.Header>
<Modal.Content>
<AddressSummary value={account.address()} />
<Password
autoFocus
isError={!isOldValid}
label={t('your current password')}
onChange={this.onChangeOld}
tabIndex={1}
value={oldPass}
/>
<Password
isError={!isNewValid}
label={t('your new password')}
onChange={this.onChangeNew}
tabIndex={2}
value={newPass}
/>
<AddressRow
isInline
value={account.address()}
>
<p>{t('This will apply to any future use of this account as stored on this browser. Ensure that you securely store this new password and that it is strong and unique to the account.')}</p>
<div>
<Password
autoFocus
help={t('The existing account password as specified when this account was created or when it was last changed.')}
isError={!isOldValid}
label={t('your current password')}
onChange={this.onChangeOld}
tabIndex={1}
value={oldPass}
/>
<Password
help={t('The new account password. Once set, all future account unlocks will be performed with this new password.')}
isError={!isNewValid}
label={t('your new password')}
onChange={this.onChangeNew}
tabIndex={2}
value={newPass}
/>
</div>
</AddressRow>
</Modal.Content>
</>
);

View File

@ -5,9 +5,9 @@
import { I18nProps } from '@polkadot/ui-app/types';
import React from 'react';
import { AddressSummary, Button, Modal } from '@polkadot/ui-app';
import { AddressRow, Button, Modal } from '@polkadot/ui-app';
import translate from './translate';
import translate from '../translate';
type Props = I18nProps & {
address: string,
@ -22,7 +22,6 @@ class Forgetting extends React.PureComponent<Props> {
className='accounts--Forgetting-Modal'
dimmer='inverted'
open
size='tiny'
>
{this.renderContent()}
{this.renderButtons()}
@ -30,7 +29,7 @@ class Forgetting extends React.PureComponent<Props> {
);
}
renderButtons () {
private renderButtons () {
const { onClose, doForget, t } = this.props;
return (
@ -52,7 +51,7 @@ class Forgetting extends React.PureComponent<Props> {
);
}
renderContent () {
private renderContent () {
const { address, t } = this.props;
return (
@ -61,7 +60,13 @@ class Forgetting extends React.PureComponent<Props> {
{t('Confirm account removal')}
</Modal.Header>
<Modal.Content>
<AddressSummary value={address} />
<AddressRow
isInline
value={address}
>
<p>{t('You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.')}</p>
<p>{t('This operaion does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the account on this browser.')}</p>
</AddressRow>
</Modal.Content>
</>
);

View File

@ -6,7 +6,7 @@ import { KeyringAddress } from '@polkadot/ui-keyring/types';
import { I18nProps } from '@polkadot/ui-app/types';
import React from 'react';
import { AddressSummary, Button, Modal } from '@polkadot/ui-app';
import { AddressRow, Button, Modal } from '@polkadot/ui-app';
import translate from './translate';
@ -31,7 +31,6 @@ class Forgetting extends React.PureComponent<Props> {
return (
<Modal
size='tiny'
dimmer='inverted'
open
style={style}
@ -42,7 +41,7 @@ class Forgetting extends React.PureComponent<Props> {
);
}
renderButtons () {
private renderButtons () {
const { onClose, doForget, t } = this.props;
return (
@ -64,7 +63,7 @@ class Forgetting extends React.PureComponent<Props> {
);
}
renderContent () {
private renderContent () {
const { t, currentAddress } = this.props;
const address = currentAddress
@ -77,10 +76,13 @@ class Forgetting extends React.PureComponent<Props> {
{t('Confirm address removal')}
</Modal.Header>
<Modal.Content className='forgetting-Address'>
<AddressSummary
className='ui--AddressSummary-base'
<AddressRow
isInline
value={address || ''}
/>
>
<p>{t('You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.')}</p>
<p>{t('This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the address on this browser.')}</p>
</AddressRow>
</Modal.Content>
</>
);

View File

@ -26,7 +26,6 @@ type Props = ApiProps & I18nProps & {
controllerId?: string | null,
filter: AccountFilter,
isValidator: boolean,
name: string,
recentlyOffline: RecentlyOfflineMap,
sessionId?: string | null,
stashId?: string | null,
@ -58,7 +57,7 @@ class Account extends React.PureComponent<Props, State> {
};
render () {
const { accountId, controllerId, filter, name, stashId } = this.props;
const { accountId, controllerId, filter, stashId } = this.props;
if ((filter === 'controller' && !stashId) || (filter === 'stash' && !controllerId) || (filter === 'unbonded' && (controllerId || stashId))) {
return null;
@ -77,7 +76,6 @@ class Account extends React.PureComponent<Props, State> {
{this.renderUnbond()}
{this.renderValidating()}
<AddressSummary
name={name}
value={accountId}
identIconSize={96}
withAvailable

View File

@ -59,6 +59,7 @@ class Accounts extends React.PureComponent<Props, State> {
const { balances, recentlyOffline, t, validators } = this.props;
const { filter, filterOptions } = this.state;
const accounts = keyring.getAccounts();
const stashOptions = this.getStashOptions();
return (
<Wrapper>
@ -74,7 +75,6 @@ class Accounts extends React.PureComponent<Props, State> {
<div className='accounts'>
{accounts.map((account) => {
const address = account.address();
const name = account.getMeta().name || '';
return (
<Account
@ -83,9 +83,8 @@ class Accounts extends React.PureComponent<Props, State> {
filter={filter}
isValidator={validators.includes(address)}
key={address}
name={name}
recentlyOffline={recentlyOffline}
stashOptions={this.getStashOptions()}
stashOptions={stashOptions}
/>
);
})}

View File

@ -10,7 +10,6 @@ import React from 'react';
import { AccountId, Balance, Option, StakingLedger, Exposure } from '@polkadot/types';
import { withCalls, withMulti } from '@polkadot/ui-api/with';
import { AddressMini, AddressRow, RecentlyOffline } from '@polkadot/ui-app';
import { getAddrName } from '@polkadot/ui-app/util';
import { formatBalance } from '@polkadot/util';
import keyring from '@polkadot/ui-keyring';
@ -76,7 +75,7 @@ class Address extends React.PureComponent<Props, State> {
}
render () {
const { address, lastAuthor, lastBlock, stashId, staking_stakers, filter } = this.props;
const { address, defaultName, lastAuthor, lastBlock, stashId, staking_stakers, filter } = this.props;
const { controllerId } = this.state;
const isAuthor = [address, controllerId, stashId].includes(lastAuthor);
const bonded = staking_stakers && !staking_stakers.own.isZero()
@ -95,7 +94,7 @@ class Address extends React.PureComponent<Props, State> {
<article key={stashId || controllerId}>
<AddressRow
bonded={bonded}
name={this.getDisplayName()}
defaultName={defaultName}
value={stashId || null}
withBalance={false}
withBonded
@ -115,16 +114,6 @@ class Address extends React.PureComponent<Props, State> {
);
}
private getDisplayName = (): string | undefined => {
const { defaultName, stashId } = this.props;
if (!stashId) {
return defaultName;
}
return getAddrName(stashId) || defaultName;
}
private renderKeys () {
const { t } = this.props;
const { controllerId, sessionId } = this.state;

View File

@ -11,11 +11,11 @@ import translate from './translate';
class AddressRow extends AddressSummary {
render () {
const { className, style, identIconSize = 64, value } = this.props;
const { className, style, identIconSize = 64, isInline, value } = this.props;
return (
<div
className={classes('ui--AddressRow', !value && 'invalid', className)}
className={classes('ui--AddressRow', !value && 'invalid', isInline && 'inline', className)}
style={style}
>
<div className='ui--AddressRow-base'>

View File

@ -12,7 +12,7 @@ import { withCalls } from '@polkadot/ui-api';
import BaseIdentityIcon from '@polkadot/ui-identicon';
import AvailableDisplay from './Available';
import { classes, toShortAddress } from './util';
import { classes, getAddrName, toShortAddress } from './util';
import BalanceDisplay from './Balance';
import BondedDisplay from './Bonded';
import IdentityIcon from './IdentityIcon';
@ -24,10 +24,11 @@ export type Props = I18nProps & {
balance?: BN | Array<BN>,
bonded?: BN | Array<BN>,
children?: React.ReactNode,
defaultName?: string,
extraInfo?: React.ReactNode,
identIconSize?: number,
isInline?: boolean,
isShort?: boolean,
name?: string,
session_validators?: Array<AccountId>,
value: AccountId | AccountIndex | Address | string | null,
withAvailable?: boolean,
@ -44,13 +45,13 @@ const DEFAULT_ADDR = '5'.padEnd(16, 'x');
class AddressSummary extends React.PureComponent<Props> {
render () {
const { accounts_idAndIndex = [], className, style } = this.props;
const { accounts_idAndIndex = [], className, isInline, style } = this.props;
const [accountId, accountIndex] = accounts_idAndIndex;
const isValid = accountId || accountIndex;
return (
<div
className={classes('ui--AddressSummary', !isValid && 'invalid', className)}
className={classes('ui--AddressSummary', !isValid && 'invalid', isInline && 'inline', className)}
style={style}
>
<div className='ui--AddressSummary-base'>
@ -69,13 +70,14 @@ class AddressSummary extends React.PureComponent<Props> {
}
protected renderAddress () {
const { name, isShort = true, value } = this.props;
const { defaultName, isShort = true, value } = this.props;
if (!value) {
return null;
}
const address = value.toString();
const name = getAddrName(address, false, defaultName);
return (
<div className='ui--AddressSummary-data'>
@ -94,7 +96,7 @@ class AddressSummary extends React.PureComponent<Props> {
}
protected renderAccountId () {
const { accounts_idAndIndex = [], name, isShort = true, value } = this.props;
const { accounts_idAndIndex = [], defaultName, isShort = true, value } = this.props;
const [_accountId, accountIndex] = accounts_idAndIndex;
const accountId = _accountId || value;
@ -105,6 +107,7 @@ class AddressSummary extends React.PureComponent<Props> {
const address = accountId
? accountId.toString()
: DEFAULT_ADDR;
const name = getAddrName(address, false, defaultName);
return (
<div className='ui--AddressSummary-data'>

View File

@ -36,6 +36,7 @@ const Wrapper = styled.div`
margin: 0.25rem 0 0 0;
padding-right: 0.5rem;
position: relative;
text-align: left;
i.icon.help {
margin: 0 0 0 0.25rem;

View File

@ -114,8 +114,18 @@ header .ui--Button-Group {
vertical-align: top;
}
.ui--AddressRow .ui--AddressSummary-children {
display: block;
.ui--AddressRow {
&.inline {
display: flex;
.ui--AddressSummary-children {
padding: 0 0 0 3rem;
}
}
.ui--AddressSummary-children {
display: block;
}
}
.ui--AddressSummary .rx--updated {

View File

@ -85,6 +85,7 @@
.ui.inverted.dimmer {
background-color: rgba(255, 255, 255, 0.75);
padding: 0 1rem 1rem;
}
.ui.label:not(.ui--Bubble) {
@ -100,8 +101,7 @@
font-family: sans-serif;
> .actions,
> .content,
> .header {
> .content {
background: transparent;
}
@ -114,9 +114,17 @@
text-align: center;
}
> .header {
border-bottom: none;
padding-bottom: 0;
> .header:not(.ui) {
background: #f5f5f5;
font-size: 1.25rem !important;
font-weight: normal;
line-height: 1.25rem;
padding: 1rem 1.5rem;
}
> :first-child:not(.icon) {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.description {
@ -194,4 +202,3 @@
.ui.segment {
background: transparent;
}

View File

@ -43,6 +43,7 @@
background-color: $button-positive-color;
color: white;
}
.ui.positive.button:hover,
.ui.buttons .positive.button:hover {
background-color: $button-positive-color-hover;
@ -73,8 +74,8 @@
background-color: $progress-bar-color;
}
.ui--IdentityIcon .container.highlight:before {
box-shadow: 0 0 5px 2px $wild-orchid;
.ui.modal > .header:not(.ui) {
border-bottom-color: $progress-bar-color;
}
a {

View File

@ -5,14 +5,20 @@
import keyring from '@polkadot/ui-keyring';
import toShortAddress from './toShortAddress';
export default function getAddrName (address: string, withShort?: boolean): string | undefined {
const pair = keyring.getAccount(address).isValid()
? keyring.getAccount(address)
: keyring.getAddress(address);
export default function getAddrName (address: string, withShort?: boolean, defaultName?: string): string | undefined {
let pair;
const name = pair.isValid()
try {
pair = keyring.getAccount(address).isValid()
? keyring.getAccount(address)
: keyring.getAddress(address);
} catch (error) {
// all-ok, we have empty fallbacks
}
const name = pair && pair.isValid()
? pair.getMeta().name
: undefined;
: defaultName;
return !name && withShort
? toShortAddress(address)