Adjust address displays in democracy (#2469)

* Adjust address displays in democracy

* Additional adjustments

* Some settings tweaks
This commit is contained in:
Jaco Greeff 2020-03-28 07:28:39 +01:00 committed by GitHub
parent 07a48692e6
commit fad6571b8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 212 additions and 177 deletions

View File

@ -25,9 +25,9 @@
"No upgradable extensions found": "No upgradable extensions found",
"upgradable extensions": "upgradable extensions",
"Update metadata": "Update metadata",
"Extensions {{count}}": "Extensions {{count}}",
"Chain Specifications as a QR Code": "Chain Specifications as a QR Code",
"Generate Random Color": "Generate Random Color",
"Extensions": "Extensions",
"Chain specifications": "Chain specifications",
"Random": "Random",
"Network Name": "Network Name",
"Name of the network. It only for display purpose.": "Name of the network. It only for display purpose.",
"Color": "Color",
@ -40,6 +40,6 @@
"Prefix indicates the ss58 address format in this network, is a number between 0 ~ 255 describes the precise format of the bytes of the address": "Prefix indicates the ss58 address format in this network, is a number between 0 ~ 255 describes the precise format of the bytes of the address",
"Decimals": "Decimals",
"Decimals decides the smallest unit of the token, which is 1/10^decimals": "Decimals decides the smallest unit of the token, which is 1/10^decimals",
"Metadata": "Metadata",
"Metadata {{count}}": "Metadata {{count}}",
"Prefix indicates the his network, is a number between 0 ~ 255 describes the precise format of the bytes of the address": "Prefix indicates the his network, is a number between 0 ~ 255 describes the precise format of the bytes of the address"
}

View File

@ -25,9 +25,9 @@
"No upgradable extensions found": "没有需要升级的浏览器插件",
"upgradable extensions": "升级浏览器插件",
"Update metadata": "升级Metadata",
"Extensions {{count}}": "浏览器插件 {{count}}",
"Chain Specifications as a QR Code": "生成含网络参数的二维码",
"Generate Random Color": "随机生成颜色",
"Extensions": "浏览器插件",
"Chain specifications": "生成含网络参数的二维码",
"Random": "随机生成颜色",
"Network Name": "网络名称",
"Name of the network. It only for display purpose.": "网络名称,仅作为显示用",
"Color": "颜色",

View File

@ -28,6 +28,10 @@ function DispatchEntry ({ value: { at, image, imageHash, index } }: Props): Reac
return (
<tr>
<td className='number top'><h1>{formatNumber(index)}</h1></td>
<ProposalCell
imageHash={imageHash}
proposal={image?.proposal}
/>
<td className='number together top'>
{bestNumber && (
<>
@ -37,10 +41,6 @@ function DispatchEntry ({ value: { at, image, imageHash, index } }: Props): Reac
</>
)}
</td>
<ProposalCell
imageHash={imageHash}
proposal={image?.proposal}
/>
<td className='together number top'>
{!image?.proposal && (
<PreImageButton

View File

@ -21,7 +21,7 @@ function DispatchQueue ({ className }: Props): React.ReactElement<Props> | null
const queued = useCall<DeriveDispatch[]>(api.derive.democracy.dispatchQueue as any, []);
return (
<div className={className}>
<div className={`proposalSection ${className}`}>
<h1>{t('dispatch queue')}</h1>
{queued
? queued.length

View File

@ -5,7 +5,7 @@
import { DeriveProposalExternal } from '@polkadot/api-derive/types';
import React from 'react';
import { AddressSmall, Button } from '@polkadot/react-components';
import { AddressMini, Button } from '@polkadot/react-components';
import { FormatBalance } from '@polkadot/react-query';
import { useTranslation } from '../translate';
@ -23,17 +23,16 @@ function External ({ className, value: { image, imageHash, threshold } }: Props)
return (
<tr className={className}>
<td className='top'>
{image && <AddressSmall value={image.proposer} />}
<ProposalCell
imageHash={imageHash}
proposal={image?.proposal}
/>
<td className='address'>
{image && <AddressMini label={t('proposer')} value={image.proposer} />}
</td>
<td className='number together top'>
{image && <FormatBalance label={<label>{t('locked')}</label>} value={image.balance} />}
</td>
<ProposalCell
className='top'
imageHash={imageHash}
proposal={image?.proposal}
/>
<td className='together number top'>
<Button.Group>
{!image?.proposal && (

View File

@ -6,7 +6,7 @@ import { DeriveProposal } from '@polkadot/api-derive/types';
import React from 'react';
import styled from 'styled-components';
import { AddressMini, AddressSmall, Button, Expander, LinkExternal } from '@polkadot/react-components';
import { AddressMini, Button, Expander, LinkExternal } from '@polkadot/react-components';
import { FormatBalance } from '@polkadot/react-query';
import { formatNumber } from '@polkadot/util';
@ -27,17 +27,16 @@ function Proposal ({ className, value: { balance, index, image, imageHash, propo
return (
<tr className={className}>
<td className='number top'><h1>{formatNumber(index)}</h1></td>
<ProposalCell
imageHash={imageHash}
proposal={image?.proposal}
/>
<td className='address'>
<AddressSmall value={proposer} />
<AddressMini label={t('proposer')} value={proposer} />
</td>
<td className='number together top'>
<FormatBalance label={<label>{t('locked')}</label>} value={balance} />
</td>
<ProposalCell
className='top'
imageHash={imageHash}
proposal={image?.proposal}
/>
<td className='top padtop'>
{seconding.length !== 0 && (
<Expander summary={t('Seconds ({{count}})', { replace: { count: seconding.length } })}>

View File

@ -2,17 +2,22 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ChainInfo } from '../types';
import React, { useCallback, useMemo, useState } from 'react';
import { Button, Dropdown } from '@polkadot/react-components';
import { Button, Dropdown, Spinner } from '@polkadot/react-components';
import { useToggle } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
import useChainInfo from '../useChainInfo';
import useExtensions from '../useExtensions';
function Extensions (): React.ReactElement {
interface Props {
chainInfo: ChainInfo | null;
className?: string;
}
function Extensions ({ chainInfo, className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const metaDef = useChainInfo();
const { extensions } = useExtensions();
const [selectedIndex, setSelectedIndex] = useState(0);
const [isBusy, toggleBusy] = useToggle();
@ -22,38 +27,43 @@ function Extensions (): React.ReactElement {
);
const _updateMeta = useCallback(
(): void => {
if (metaDef && extensions?.[selectedIndex]) {
if (chainInfo && extensions?.[selectedIndex]) {
toggleBusy();
extensions[selectedIndex]
.update(metaDef)
.update(chainInfo)
.catch(() => false)
.then(() => toggleBusy());
}
},
[extensions, metaDef, selectedIndex, toggleBusy]
[chainInfo, extensions, selectedIndex, toggleBusy]
);
if (!options.length) {
return <div>{t('No upgradable extensions found')}</div>;
}
return (
<div>
<Dropdown
label={t('upgradable extensions')}
onChange={setSelectedIndex}
options={options}
value={selectedIndex}
/>
<Button.Group>
<Button
icon='refresh'
isDisabled={isBusy}
label={t('Update metadata')}
onClick={_updateMeta}
/>
</Button.Group>
<div className={className}>
{extensions
? options.length
? (
<>
<Dropdown
label={t('upgradable extensions')}
onChange={setSelectedIndex}
options={options}
value={selectedIndex}
/>
<Button.Group>
<Button
icon='upload'
isDisabled={isBusy}
label={t('Update metadata')}
onClick={_updateMeta}
/>
</Button.Group>
</>
)
: t('No upgradable extensions found')
: <Spinner />
}
</div>
);
}

View File

@ -2,40 +2,46 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Button, Input } from '@polkadot/react-components';
import { BareProps } from '@polkadot/react-components/types';
import { ChainInfo } from '../types';
import React, { useCallback, useEffect, useReducer, useState } from 'react';
import styled from 'styled-components';
import { Button, Columar, Column, Input, Spinner } from '@polkadot/react-components';
import { useApi, useDebounce } from '@polkadot/react-hooks';
import { QrNetworkSpecs } from '@polkadot/react-qr';
import { NetworkSpecsStruct } from '@polkadot/ui-settings';
import React, { useEffect, useReducer, useState } from 'react';
import styled from 'styled-components';
import ChainColorIndicator from './ChainColorIndicator';
import { useTranslation } from '../translate';
import useChainInfo from '../useChainInfo';
interface Props {
chainInfo: ChainInfo | null;
className?: string;
}
function getRandomColor (): string {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
function NetworkSpecs ({ className }: BareProps): React.ReactElement<BareProps> | null {
const initialState = {
decimals: 0,
prefix: 0,
unit: 'UNIT',
title: '',
color: '#FFFFFF',
genesisHash: ''
};
function NetworkSpecs ({ chainInfo, className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { isApiReady, systemChain } = useApi();
const chainInfo = useChainInfo();
const initialState = {
decimals: 0,
prefix: 0,
unit: 'UNIT',
title: '',
color: '#FFFFFF',
genesisHash: ''
};
const [qrData, setQrData] = useState<NetworkSpecsStruct>(initialState);
const debouncedQrData = useDebounce(qrData, 500);
const reducer = (state: NetworkSpecsStruct, delta: Partial<NetworkSpecsStruct>): NetworkSpecsStruct => {
@ -49,98 +55,110 @@ function NetworkSpecs ({ className }: BareProps): React.ReactElement<BareProps>
const [networkSpecs, setNetworkSpecs] = useReducer(reducer, initialState);
useEffect((): void => {
if (chainInfo != null) {
setNetworkSpecs({
color: chainInfo.color || getRandomColor(),
decimals: chainInfo.tokenDecimals,
prefix: chainInfo.ss58Format,
unit: chainInfo.tokenSymbol,
title: systemChain,
genesisHash: chainInfo.genesisHash
});
}
chainInfo && setNetworkSpecs({
color: chainInfo.color || getRandomColor(),
decimals: chainInfo.tokenDecimals,
prefix: chainInfo.ss58Format,
unit: chainInfo.tokenSymbol,
title: systemChain,
genesisHash: chainInfo.genesisHash
});
}, [chainInfo, systemChain]);
const _onChangeColor = useCallback(
(color: string): void => setNetworkSpecs({ color }),
[]
);
const _onSetRandomColor = useCallback(
(): void => setNetworkSpecs({ color: getRandomColor() }),
[]
);
const _checkColorValid = useCallback(
(): boolean => /^#[\da-fA-F]{6}|#[\da-fA-F]{3}$/.test(networkSpecs.color),
[networkSpecs]
);
if (!isApiReady) {
return null;
return <Spinner />;
}
type inputListener = (v: string) => void;
const _onChangeValue = (k: keyof NetworkSpecsStruct): inputListener => (v: string): void => setNetworkSpecs({ [k]: v });
const _onSetRandomColor = (): void => setNetworkSpecs({ color: getRandomColor() });
const _checkColorValid = (): boolean => /^#[\da-fA-F]{6}|#[\da-fA-F]{3}$/.test(networkSpecs.color);
return (
<div className={className}>
<Input
autoFocus
className='full'
help={t('Name of the network. It only for display purpose.')}
label={t('Network Name')}
onChange={_onChangeValue('title')}
value={networkSpecs.title}
/>
<Input
className='full'
help={t('The color used to distinguish this network with others, use color code with 3 or 6 digits, like "#FFF" or "#111111"')}
isError={!_checkColorValid()}
label={t('Color')}
onChange={_onChangeValue('color')}
value={networkSpecs.color}
>
<div className='settings--networkSpecs-colorButton'>
<Button
label={t('Generate Random Color')}
icon='sync'
key='spread'
onClick={_onSetRandomColor}
/>
<ChainColorIndicator color={networkSpecs.color}/>
</div>
</Input>
<Input
className='full'
help={t('Genesis Hash refers to initial state of the chain, it cannot be changed once the chain is launched')}
isDisabled
label={t('Genesis Hash')}
value={networkSpecs.genesisHash}
/>
<Input
className='full'
help={t('Unit decides the name of 1 unit token, e.g. "DOT" for Polkadot')}
isDisabled
label={t('Unit')}
value={networkSpecs.unit}
/>
<Input
className='full'
help={t('Prefix indicates the his network, is a number between 0 ~ 255 describes the precise format of the bytes of the address')}
isDisabled
label={t('Address Prefix')}
value={networkSpecs.prefix}
/>
<Input
className='full'
help={t('Decimals decides the smallest unit of the token, which is 1/10^decimals')}
isDisabled
label={t('Decimals')}
value={networkSpecs.decimals}
/>
<QrNetworkSpecs className='settings--networkSpecs-qr' networkSpecs={debouncedQrData}/>
</div>
<Columar className={className}>
<Column>
<Input
className='full'
help={t('Name of the network. It only for display purpose.')}
isDisabled
label={t('Network Name')}
value={networkSpecs.title}
/>
<Input
className='full'
help={t('The color used to distinguish this network with others, use color code with 3 or 6 digits, like "#FFF" or "#111111"')}
isError={!_checkColorValid()}
label={t('Color')}
onChange={_onChangeColor}
value={networkSpecs.color}
>
<div className='settings--networkSpecs-colorButton'>
<Button
label={t('Random')}
icon='sync'
key='spread'
onClick={_onSetRandomColor}
/>
<ChainColorIndicator color={networkSpecs.color}/>
</div>
</Input>
<Input
className='full'
help={t('Genesis Hash refers to initial state of the chain, it cannot be changed once the chain is launched')}
isDisabled
label={t('Genesis Hash')}
value={networkSpecs.genesisHash}
/>
<Input
className='full'
help={t('Unit decides the name of 1 unit token, e.g. "DOT" for Polkadot')}
isDisabled
label={t('Unit')}
value={networkSpecs.unit}
/>
<Input
className='full'
help={t('Prefix indicates the his network, is a number between 0 ~ 255 describes the precise format of the bytes of the address')}
isDisabled
label={t('Address Prefix')}
value={networkSpecs.prefix}
/>
<Input
className='full'
help={t('Decimals decides the smallest unit of the token, which is 1/10^decimals')}
isDisabled
label={t('Decimals')}
value={networkSpecs.decimals}
/>
</Column>
<Column>
<QrNetworkSpecs
className='settings--networkSpecs-qr'
networkSpecs={debouncedQrData}
/>
</Column>
</Columar>
);
}
export default React.memo(styled(NetworkSpecs)`
top: .3rem;
.settings--networkSpecs-colorButton {
display: flex;
flex-direction: row;
}
.settings--networkSpecs-qr {
margin: 2rem auto;
margin: 0.25rem auto;
max-width: 15rem;
}
`);

View File

@ -2,30 +2,23 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { useTranslation } from '@polkadot/app-settings/translate';
import useCounter from '@polkadot/app-settings/useCounter';
import React, { useMemo } from 'react';
import React from 'react';
import useChainInfo from '../useChainInfo';
import { useTranslation } from '../translate';
import Extensions from './Extensions';
import NetworkSpecs from './NetworkSpecs';
export default function Metadata (): React.ReactElement {
const { t } = useTranslation();
const numExtensions = useCounter();
const extensionsTitle = useMemo(() => t('Extensions {{count}}', {
replace: {
count: numExtensions
? `(${numExtensions})`
: ''
}
}), [numExtensions, t]);
const chainInfo = useChainInfo();
return (
<>
<h1>{extensionsTitle}</h1>
<Extensions />
<h1>{t('Chain Specifications as a QR Code')}</h1>
<NetworkSpecs />
<h1>{t('Extensions')}</h1>
<Extensions chainInfo={chainInfo} />
<h1>{t('Chain specifications')}</h1>
<NetworkSpecs chainInfo={chainInfo} />
</>
);
}

View File

@ -4,7 +4,7 @@
import { AppProps as Props } from '@polkadot/react-components/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Route, Switch } from 'react-router';
import { HelpOverlay, Tabs } from '@polkadot/react-components';
import uiSettings from '@polkadot/ui-settings';
@ -24,7 +24,8 @@ const hidden = uiSettings.uiMode === 'full'
function SettingsApp ({ basePath, onStatusChange }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const items = [
const numExtensions = useCounter();
const items = useMemo(() => [
{
isRoot: true,
name: 'general',
@ -32,13 +33,17 @@ function SettingsApp ({ basePath, onStatusChange }: Props): React.ReactElement<P
},
{
name: 'metadata',
text: t('Metadata')
text: t('Metadata {{count}}', {
replace: {
count: numExtensions ? `(${numExtensions})` : ''
}
})
},
{
name: 'developer',
text: t('Developer')
}
];
], [numExtensions, t]);
return (
<main className='settings--App'>

View File

@ -0,0 +1,9 @@
// Copyright 2017-2020 @polkadot/app-settings 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 { MetadataDef } from '@polkadot/extension-inject/types';
export interface ChainInfo extends MetadataDef {
color: string | undefined;
}

View File

@ -1,19 +1,15 @@
/* eslint-disable @typescript-eslint/no-empty-interface */
// Copyright 2017-2020 @polkadot/app-settings 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 { MetadataDef } from '@polkadot/extension-inject/types';
import { ChainInfo } from './types';
import { getChainTypes } from '@polkadot/apps-config/api';
import { getSystemChainColor, getSystemIcon } from '@polkadot/apps-config/ui';
import { useApi } from '@polkadot/react-hooks';
import { useEffect, useState } from 'react';
interface ChainInfo extends MetadataDef {
color: string | undefined;
}
export default function useChainInfo (): ChainInfo | null {
const { api, isApiReady, systemChain, systemName } = useApi();
const [state, setState] = useState<ChainInfo | null>(null);

View File

@ -256,9 +256,8 @@ function Account ({ allStashes, className, isOwnStash, next, onUpdateType, rewar
)}
<AddressSmall value={stashId} />
</td>
<td className='top'>
<td className='address'>
<AddressMini
className='mini-nopad'
label={t('controller')}
value={controllerId}
/>
@ -472,8 +471,4 @@ export default React.memo(styled(Account)`
margin-right: 0.25rem;
vertical-align: inherit;
}
.mini-nopad {
padding: 0;
}
`);

View File

@ -38,7 +38,7 @@ function ProposalDisplay ({ className, isMember, proposal: { council, id, propos
value={proposal.bond}
/>
</td>
<td className='top'>
<td className='address'>
<AddressMini
label={t('beneficiary')}
value={proposal.beneficiary}

View File

@ -106,6 +106,17 @@ const Memo = React.memo(styled(Table)`
&.address {
padding: 0.85rem 1rem;
vertical-align: top;
> .ui--AddressMini,
> .ui--AddressMini.padded {
padding: 0;
margin-bottom: -0.85rem;
margin-top: -0.25rem;
label.ui--AddressMini-label {
margin-bottom: -0.3rem;
}
}
}
&.number {