Convert options & items (tabs) to useMemo (#2207)

* Convert options & items (tabs) to useMemo

* Add missing var in useMemo

* Remove floating isSubstrateV2

* Cleanup staking entry

* Remove unneeded ?
This commit is contained in:
Jaco Greeff 2020-01-28 15:01:18 +01:00 committed by GitHub
parent d2816155c7
commit 1d8613655f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 285 additions and 307 deletions

View File

@ -5,7 +5,7 @@
import { AppProps as Props } from '@polkadot/react-components/types';
import { ComponentProps } from './types';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { Route, Switch } from 'react-router';
import { useAccounts } from '@polkadot/react-hooks';
import { HelpOverlay, Tabs } from '@polkadot/react-components';
@ -19,6 +19,17 @@ export default function AccountsApp ({ basePath, onStatusChange }: Props): React
const { t } = useTranslation();
const { hasAccounts } = useAccounts();
const [hidden, setHidden] = useState<string[]>(['vanity']);
const items = useMemo(() => [
{
isRoot: true,
name: 'overview',
text: t('My accounts')
},
{
name: 'vanity',
text: t('Vanity address')
}
], [t]);
useEffect((): void => {
setHidden(
@ -42,17 +53,7 @@ export default function AccountsApp ({ basePath, onStatusChange }: Props): React
<Tabs
basePath={basePath}
hidden={hidden}
items={[
{
isRoot: true,
name: 'overview',
text: t('My accounts')
},
{
name: 'vanity',
text: t('Vanity address')
}
]}
items={items}
/>
</header>
<Switch>

View File

@ -8,7 +8,7 @@ import { KeypairType } from '@polkadot/util-crypto/types';
import { ModalProps } from '../types';
import FileSaver from 'file-saver';
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import styled from 'styled-components';
import { DEV_PHRASE } from '@polkadot/keyring/defaults';
import { AddressRow, Button, Dropdown, Input, InputAddress, Modal, Password } from '@polkadot/react-components';
@ -164,6 +164,14 @@ function Create ({ className, onClose, onStatusChange, seed: propsSeed, type: pr
const [{ isNameValid, name }, setName] = useState({ isNameValid: false, name: '' });
const [{ isPassValid, password }, setPassword] = useState({ isPassValid: false, password: '' });
const isValid = !!address && !deriveError && isNameValid && isPassValid && isSeedValid;
const seedOpt = useMemo(() => (
isDevelopment
? [{ value: 'dev', text: t('Development') }]
: []
).concat(
{ value: 'bip', text: t('Mnemonic') },
{ value: 'raw', text: t('Raw seed') }
), [isDevelopment, t]);
const _onChangePass = (password: string): void =>
setPassword({ isPassValid: keyring.isPassValid(password), password });
@ -245,16 +253,7 @@ function Create ({ className, onClose, onStatusChange, seed: propsSeed, type: pr
isButton
defaultValue={seedType}
onChange={_selectSeedType}
options={
(
isDevelopment
? [{ value: 'dev', text: t('Development') }]
: []
).concat(
{ value: 'bip', text: t('Mnemonic') },
{ value: 'raw', text: t('Raw seed') }
)
}
options={seedOpt}
/>
</Input>
<Password

View File

@ -5,7 +5,7 @@
import { AppProps } from '@polkadot/react-components/types';
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Route, Switch } from 'react-router';
import { HelpOverlay } from '@polkadot/react-components';
import Tabs from '@polkadot/react-components/Tabs';
@ -21,6 +21,13 @@ interface Props extends AppProps {
export default function AddressBookApp ({ basePath, onStatusChange }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const items = useMemo(() => [
{
isRoot: true,
name: 'overview',
text: t('My contacts')
}
], [t]);
return (
<main className='address-book--App'>
@ -28,13 +35,7 @@ export default function AddressBookApp ({ basePath, onStatusChange }: Props): Re
<header>
<Tabs
basePath={basePath}
items={[
{
isRoot: true,
name: 'overview',
text: t('My contacts')
}
]}
items={items}
/>
</header>
<Switch>

View File

@ -5,7 +5,7 @@
import { DerivedCollectiveProposals } from '@polkadot/api-derive/types';
import { AppProps, BareProps } from '@polkadot/react-components/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Route, Switch } from 'react-router';
import { useLocation } from 'react-router-dom';
import styled from 'styled-components';
@ -27,27 +27,28 @@ function CouncilApp ({ basePath, className }: Props): React.ReactElement<Props>
const { pathname } = useLocation();
const numMotions = useCounter();
const motions = useCall<DerivedCollectiveProposals>(api.derive.council.proposals, []);
const items = useMemo(() => [
{
isRoot: true,
name: 'overview',
text: t('Council overview')
},
{
name: 'candidates',
text: t('Candidates')
},
{
name: 'motions',
text: t('Motions ({{count}})', { replace: { count: numMotions } })
}
], [numMotions, t]);
return (
<main className={className}>
<header>
<Tabs
basePath={basePath}
items={[
{
isRoot: true,
name: 'overview',
text: t('Council overview')
},
{
name: 'candidates',
text: t('Candidates')
},
{
name: 'motions',
text: t('Motions ({{count}})', { replace: { count: numMotions } })
}
]}
items={items}
/>
</header>
<Switch>

View File

@ -11,11 +11,6 @@ import { isBoolean } from '@polkadot/util';
import { useTranslation } from '../translate';
interface Option {
text: string;
value: number;
}
interface Props {
proposal?: Proposal;
referendumId: PropIndex;
@ -28,17 +23,15 @@ export default function Voting ({ proposal, referendumId }: Props): React.ReactE
const [conviction, setConviction] = useState(0);
const [isVotingOpen, toggleVoting] = useToggle();
const [aye, setVoteValue] = useState(true);
const convictionOpts = useMemo((): Option[] => {
return [
{ text: t('0.1x of voting balance, no lockup period'), value: 0 },
{ text: t('1x of voting balance, locked for 1x enactment'), value: 1 },
{ text: t('2x of voting balance, locked for 2x enactment'), value: 2 },
{ text: t('3x of voting balance, locked for 4x enactment'), value: 3 },
{ text: t('4x of voting balance, locked for 8x enactment'), value: 4 },
{ text: t('5x of voting balance, locked for 16x enactment'), value: 5 },
{ text: t('6x of voting balance, locked for 32x enactment'), value: 6 }
];
}, [t]);
const convictionOpts = useMemo(() => [
{ text: t('0.1x of voting balance, no lockup period'), value: 0 },
{ text: t('1x of voting balance, locked for 1x enactment'), value: 1 },
{ text: t('2x of voting balance, locked for 2x enactment'), value: 2 },
{ text: t('3x of voting balance, locked for 4x enactment'), value: 3 },
{ text: t('4x of voting balance, locked for 8x enactment'), value: 4 },
{ text: t('5x of voting balance, locked for 16x enactment'), value: 5 },
{ text: t('6x of voting balance, locked for 32x enactment'), value: 6 }
], [t]);
if (!hasAccounts) {
return null;

View File

@ -4,7 +4,7 @@
import { AppProps, BareProps } 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';
@ -23,6 +23,13 @@ const hidden = uiSettings.uiMode === 'full'
export default function DemocracyApp ({ basePath }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const items = useMemo(() => [
{
isRoot: true,
name: 'overview',
text: t('Democracy overview')
}
], [t]);
return (
<main className='democracy--App'>
@ -31,13 +38,7 @@ export default function DemocracyApp ({ basePath }: Props): React.ReactElement<P
<Tabs
basePath={basePath}
hidden={hidden}
items={[
{
isRoot: true,
name: 'overview',
text: t('Democracy overview')
}
]}
items={items}
/>
</header>
<Switch>

View File

@ -5,7 +5,7 @@
import { AppProps, BareProps } from '@polkadot/react-components/types';
import { KeyedEvent } from './types';
import React, { useContext } from 'react';
import React, { useContext, useMemo } from 'react';
import { Route, Switch } from 'react-router';
// import styled from 'styled-components';
import Tabs from '@polkadot/react-components/Tabs';
@ -28,6 +28,26 @@ function ExplorerApp ({ basePath, className }: Props): React.ReactElement<Props>
const { api } = useApi();
const { lastHeaders } = useContext(BlockAuthorsContext);
const events = useContext(EventsContext);
const items = useMemo(() => [
{
isRoot: true,
name: 'chain',
text: t('Chain info')
},
{
hasParams: true,
name: 'query',
text: t('Block details')
},
{
name: 'forks',
text: t('Forks')
},
{
name: 'node',
text: t('Node info')
}
], [t]);
return (
<main className={className}>
@ -39,26 +59,7 @@ function ExplorerApp ({ basePath, className }: Props): React.ReactElement<Props>
? api.query.babe ? [] : ['forks']
: ['node', 'forks']
}
items={[
{
isRoot: true,
name: 'chain',
text: t('Chain info')
},
{
hasParams: true,
name: 'query',
text: t('Block details')
},
{
name: 'forks',
text: t('Forks')
},
{
name: 'node',
text: t('Node info')
}
]}
items={items}
/>
</header>
<Switch>

View File

@ -2,27 +2,28 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { AppProps, I18nProps } from '@polkadot/react-components/types';
import { AppProps as Props } from '@polkadot/react-components/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Tabs } from '@polkadot/react-components';
import Selection from './Selection';
import translate from './translate';
import { useTranslation } from './translate';
interface Props extends AppProps, I18nProps {}
export default function ExtrinsicsApp ({ basePath }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const items = useMemo(() => [{
isRoot: true,
name: 'create',
text: t('Extrinsic submission')
}], [t]);
function ExtrinsicsApp ({ basePath, t }: Props): React.ReactElement<Props> {
return (
<main className='extrinsics--App'>
<header>
<Tabs
basePath={basePath}
items={[{
isRoot: true,
name: 'create',
text: t('Extrinsic submission')
}]}
items={items}
/>
</header>
<Selection />
@ -31,5 +32,3 @@ function ExtrinsicsApp ({ basePath, t }: Props): React.ReactElement<Props> {
}
export { ExtrinsicsApp };
export default translate(ExtrinsicsApp);

View File

@ -2,36 +2,39 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { AppProps, BareProps, I18nProps } from '@polkadot/react-components/types';
import { AppProps, BareProps } from '@polkadot/react-components/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Route, Switch } from 'react-router';
import { Tabs } from '@polkadot/react-components';
import Transfer from './Transfer';
import Assets from './Assets';
import translate from './translate';
import { useTranslation } from './translate';
interface Props extends AppProps, BareProps, I18nProps {}
interface Props extends AppProps, BareProps {}
export default function AssetApp ({ basePath }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const items = useMemo(() => [
{
isRoot: true,
name: 'assets',
text: t('Assets')
},
{
name: 'transfer',
text: t('Transfer')
}
], [t]);
function AssetApp ({ basePath, t }: Props): React.ReactElement<Props> {
return (
<main className='treasury--App'>
<header>
<Tabs
basePath={basePath}
items={[
{
isRoot: true,
name: 'assets',
text: t('Assets')
},
{
name: 'transfer',
text: t('Transfer')
}
]}
items={items}
/>
</header>
<Switch>
@ -41,5 +44,3 @@ function AssetApp ({ basePath, t }: Props): React.ReactElement<Props> {
</main>
);
}
export default translate(AssetApp);

View File

@ -4,7 +4,7 @@
import { AppProps, BareProps } from '@polkadot/react-components/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Route, Switch } from 'react-router';
import { Tabs } from '@polkadot/react-components';
@ -15,19 +15,20 @@ interface Props extends AppProps, BareProps {}
export default function ParachainsApp ({ basePath }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const items = useMemo(() => [
{
isRoot: true,
name: 'overview',
text: t('Parachains overview')
}
], [t]);
return (
<main>
<header>
<Tabs
basePath={basePath}
items={[
{
isRoot: true,
name: 'overview',
text: t('Parachains overview')
}
]}
items={items}
/>
</header>
<Switch>

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';
@ -21,6 +21,17 @@ const hidden = uiSettings.uiMode === 'full'
export default function SettingsApp ({ basePath, onStatusChange }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const items = useMemo(() => [
{
isRoot: true,
name: 'general',
text: t('General')
},
{
name: 'developer',
text: t('Developer')
}
], [t]);
return (
<main className='settings--App'>
@ -29,17 +40,7 @@ export default function SettingsApp ({ basePath, onStatusChange }: Props): React
<Tabs
basePath={basePath}
hidden={hidden}
items={[
{
isRoot: true,
name: 'general',
text: t('General')
},
{
name: 'developer',
text: t('Developer')
}
]}
items={items}
/>
</header>
<Switch>

View File

@ -4,7 +4,7 @@
import { AppProps, BareProps } from '@polkadot/react-components/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Route, Switch } from 'react-router';
import { Tabs } from '@polkadot/react-components';
@ -15,19 +15,20 @@ interface Props extends AppProps, BareProps {}
export default function SocietyApp ({ basePath, className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const items = useMemo(() => [
{
isRoot: true,
name: 'overview',
text: t('Society overview')
}
], [t]);
return (
<main className={className}>
<header>
<Tabs
basePath={basePath}
items={[
{
isRoot: true,
name: 'overview',
text: t('Society overview')
}
]}
items={items}
/>
</header>
<Switch>

View File

@ -2,18 +2,17 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { I18nProps } from '@polkadot/react-components/types';
import { KeypairType } from '@polkadot/util-crypto/types';
import React, { useContext, useEffect, useState } from 'react';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { Button, Dropdown, Icon, Input, Modal, StatusContext } from '@polkadot/react-components';
import keyring from '@polkadot/ui-keyring';
import { assert, u8aToHex } from '@polkadot/util';
import { keyExtractSuri, mnemonicValidate } from '@polkadot/util-crypto';
import translate from '../../translate';
import { useTranslation } from '../../translate';
interface Props extends I18nProps {
interface Props {
isOpen?: boolean;
onClose: () => void;
}
@ -28,17 +27,25 @@ const CRYPTO_MAP: Record<string, KeypairType[]> = {
const EMPTY_KEY = '0x';
function InjectKeys ({ isOpen = true, onClose, t }: Props): React.ReactElement<Props> | null {
export default function InjectKeys ({ isOpen = true, onClose }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { queueRpc } = useContext(StatusContext);
// this needs to align with what is set as the first value in `type`
const [crypto, setCrypto] = useState<KeypairType>('sr25519');
const [publicKey, setPublicKey] = useState(EMPTY_KEY);
const [suri, setSuri] = useState('');
const [type, setType] = useState('babe');
const [keyType, setKeyType] = useState('babe');
const keyTypeOpt = useMemo(() => [
{ text: t('Aura'), value: 'aura' },
{ text: t('Babe'), value: 'babe' },
{ text: t('Grandpa'), value: 'gran' },
{ text: t('I\'m Online'), value: 'imon' },
{ text: t('Parachains'), value: 'para' }
], [t]);
useEffect((): void => {
setCrypto(CRYPTO_MAP[type][0]);
}, [type]);
setCrypto(CRYPTO_MAP[keyType][0]);
}, [keyType]);
useEffect((): void => {
try {
@ -56,13 +63,12 @@ function InjectKeys ({ isOpen = true, onClose, t }: Props): React.ReactElement<P
return null;
}
const _onSubmit = (): void => {
const _onSubmit = (): void =>
queueRpc({
rpc: { section: 'author', method: 'insertKey' } as any,
values: [type, suri, publicKey]
values: [keyType, suri, publicKey]
});
};
const _cryptoOptions = CRYPTO_MAP[type].map((value): { text: string; value: KeypairType } => ({
const _cryptoOptions = CRYPTO_MAP[keyType].map((value): { text: string; value: KeypairType } => ({
text: value === 'ed25519'
? t('ed25519, Edwards')
: t('sr15519, Schnorrkel'),
@ -83,15 +89,9 @@ function InjectKeys ({ isOpen = true, onClose, t }: Props): React.ReactElement<P
/>
<Dropdown
label={t('key type to set')}
onChange={setType}
options={[
{ text: t('Aura'), value: 'aura' },
{ text: t('Babe'), value: 'babe' },
{ text: t('Grandpa'), value: 'gran' },
{ text: t('I\'m Online'), value: 'imon' },
{ text: t('Parachains'), value: 'para' }
]}
value={type}
onChange={setKeyType}
options={keyTypeOpt}
value={keyType}
/>
<Dropdown
isDisabled={_cryptoOptions.length === 1}
@ -120,5 +120,3 @@ function InjectKeys ({ isOpen = true, onClose, t }: Props): React.ReactElement<P
</Modal>
);
}
export default translate(InjectKeys);

View File

@ -6,11 +6,10 @@ import { DerivedHeartbeats, DerivedStakingOverview } from '@polkadot/api-derive/
import { AppProps as Props } from '@polkadot/react-components/types';
import { AccountId } from '@polkadot/types/interfaces';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { Route, Switch } from 'react-router';
import { useLocation } from 'react-router-dom';
import styled from 'styled-components';
import { Option } from '@polkadot/types';
import { HelpOverlay } from '@polkadot/react-components';
import Tabs from '@polkadot/react-components/Tabs';
import { useCall, useAccounts, useApi } from '@polkadot/react-hooks';
@ -25,43 +24,51 @@ import { MAX_SESSIONS } from './constants';
import { useTranslation } from './translate';
import useSessionRewards from './useSessionRewards';
const EMPY_ACCOUNTS: string[] = [];
const EMPTY_ALL: [string[], string[]] = [EMPY_ACCOUNTS, EMPY_ACCOUNTS];
function transformStakingControllers ([stashes, controllers]: [AccountId[], Option<AccountId>[]]): [string[], string[]] {
return [
stashes.map((accountId): string => accountId.toString()),
controllers
.filter((optId): boolean => optId.isSome)
.map((accountId): string => accountId.unwrap().toString())
];
}
function StakingApp ({ basePath, className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api, isSubstrateV2 } = useApi();
const { api } = useApi();
const { hasAccounts } = useAccounts();
const { pathname } = useLocation();
const [next, setNext] = useState<string[]>([]);
const [allStashes, allControllers] = (useCall<[string[], string[]]>(api.derive.staking.controllers, [], {
defaultValue: EMPTY_ALL,
transform: transformStakingControllers
}) as [string[], string[]]);
const allStashes = useCall<string[]>(api.derive.staking.controllers, [], {
defaultValue: [],
transform: ([stashes]: [AccountId[]]): string[] =>
stashes.map((accountId): string => accountId.toString())
}) as string[];
const recentlyOnline = useCall<DerivedHeartbeats>(api.derive.imOnline.receivedHeartbeats, []);
const stakingOverview = useCall<DerivedStakingOverview>(api.derive.staking.overview, []);
const sessionRewards = useSessionRewards(MAX_SESSIONS);
const hasQueries = hasAccounts && !!(api.query.imOnline?.authoredBlocks);
const validators = stakingOverview?.validators;
const items = useMemo(() => [
{
isRoot: true,
name: 'overview',
text: t('Staking overview')
},
{
name: 'waiting',
text: t('Waiting')
},
{
name: 'returns',
text: t('Returns')
},
{
name: 'actions',
text: t('Account actions')
},
{
hasParams: true,
name: 'query',
text: t('Validator stats')
}
], [t]);
useEffect((): void => {
validators && setNext(
isSubstrateV2
// this is a V2 node currentValidators is a list of stashes
? allStashes.filter((address): boolean => !validators.includes(address as any))
// this is a V1 node currentValidators is a list of controllers
: allControllers.filter((address): boolean => !validators.includes(address as any))
stakingOverview && setNext(
allStashes.filter((address): boolean => !stakingOverview.validators.includes(address as any))
);
}, [allControllers, allStashes, validators]);
}, [allStashes, stakingOverview?.validators]);
return (
<main className={`staking--App ${className}`}>
@ -76,30 +83,7 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
: ['query']
: ['actions', 'query']
}
items={[
{
isRoot: true,
name: 'overview',
text: t('Staking overview')
},
{
name: 'waiting',
text: t('Waiting')
},
{
name: 'returns',
text: t('Returns')
},
{
name: 'actions',
text: t('Account actions')
},
{
hasParams: true,
name: 'query',
text: t('Validator stats')
}
]}
items={items}
/>
</header>
<Summary

View File

@ -4,10 +4,9 @@
import { ComponentProps, QueryTypes, ParitalQueryTypes } from '../types';
import React from 'react';
import React, { useMemo } from 'react';
import { Route, Switch } from 'react-router';
import { Tabs } from '@polkadot/react-components';
import { useApi } from '@polkadot/react-hooks';
import Consts from './Consts';
import Modules from './Modules';
@ -23,7 +22,22 @@ let id = -1;
export default function Selection ({ basePath, onAdd }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { isSubstrateV2 } = useApi();
const items = useMemo(() => [
{
isRoot: true,
name: 'modules',
text: t('Storage')
},
{
name: 'constants',
text: t('Constants')
},
{
name: 'raw',
text: t('Raw storage')
}
], [t]);
const _onAdd = (query: ParitalQueryTypes): void => onAdd({ ...query, id: ++id });
const _renderComponent = (Component: React.ComponentType<ComponentProps>): () => React.ReactNode =>
// eslint-disable-next-line react/display-name
@ -34,26 +48,7 @@ export default function Selection ({ basePath, onAdd }: Props): React.ReactEleme
<header>
<Tabs
basePath={basePath}
hidden={
isSubstrateV2
? []
: ['constants']
}
items={[
{
isRoot: true,
name: 'modules',
text: t('Storage')
},
{
name: 'constants',
text: t('Constants')
},
{
name: 'raw',
text: t('Raw storage')
}
]}
items={items}
/>
</header>
<Switch>

View File

@ -6,7 +6,7 @@
import { AppProps as Props } from '@polkadot/react-components/types';
import { ComponentProps } from './types';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { Route, Switch } from 'react-router';
import { Icon, Tabs } from '@polkadot/react-components';
import { useCall, useAccounts, useApi } from '@polkadot/react-hooks';
@ -22,6 +22,17 @@ export default function SudoApp ({ basePath }: Props): React.ReactElement<Props>
const sudoKey = useCall<string>(api.query.sudo.key, [], { transform: (k): string => k.toString() });
const { allAccounts } = useAccounts();
const [isMine, setIsMine] = useState(false);
const items = useMemo(() => [
{
isRoot: true,
name: 'index',
text: t('Sudo access')
},
{
name: 'key',
text: t('Set sudo key')
}
], [t]);
useEffect((): void => {
setIsMine(!!sudoKey && allAccounts.some((key): boolean => key === sudoKey));
@ -45,17 +56,7 @@ export default function SudoApp ({ basePath }: Props): React.ReactElement<Props>
<header>
<Tabs
basePath={basePath}
items={[
{
isRoot: true,
name: 'index',
text: t('Sudo access')
},
{
name: 'key',
text: t('Set sudo key')
}
]}
items={items}
/>
</header>
{isMine

View File

@ -5,7 +5,7 @@
import { AccountId, Hash } from '@polkadot/types/interfaces';
import { AppProps, BareProps } from '@polkadot/react-components/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Route, Switch } from 'react-router';
import { useApi, useCall } from '@polkadot/react-hooks';
import { Tabs } from '@polkadot/react-components';
@ -23,23 +23,24 @@ export default function TechCommApp ({ basePath, className }: Props): React.Reac
const { api } = useApi();
const members = useCall<AccountId[]>(api.query.technicalCommittee.members, []);
const proposals = useCall<Hash[]>(api.query.technicalCommittee.proposals, []);
const items = useMemo(() => [
{
isRoot: true,
name: 'overview',
text: t('Tech. committee')
},
{
name: 'proposals',
text: t('Proposals ({{count}})', { replace: { count: proposals?.length || 0 } })
}
], [proposals, t]);
return (
<main className={className}>
<header>
<Tabs
basePath={basePath}
items={[
{
isRoot: true,
name: 'overview',
text: t('Tech. committee')
},
{
name: 'proposals',
text: t('Proposals ({{count}})', { replace: { count: (proposals && proposals.length) || 0 } })
}
]}
items={items}
/>
</header>
<Switch>

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 Tabs from '@polkadot/react-components/Tabs';
import { useAccounts } from '@polkadot/react-hooks';
@ -18,7 +18,7 @@ import { useTranslation } from './translate';
export default function ToolboxApp ({ basePath }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { hasAccounts } = useAccounts();
const tabs = [
const items = useMemo(() => [
{
isRoot: true,
name: 'rpc',
@ -36,17 +36,19 @@ export default function ToolboxApp ({ basePath }: Props): React.ReactElement<Pro
name: 'verify',
text: t('Verify signature')
}
];
const filteredTabs = hasAccounts
? tabs
: tabs.filter(({ name }): boolean => !['sign', 'verify'].includes(name));
], [t]);
return (
<main className='toolbox--App'>
<header>
<Tabs
basePath={basePath}
items={filteredTabs}
hidden={
hasAccounts
? []
: ['sign', 'verify']
}
items={items}
/>
</header>
<Switch>

View File

@ -109,7 +109,7 @@ export default withMulti(
['query.elections.members', {
propName: 'threshold',
transform: (value: [AccountId, BlockNumber][]): number =>
1 + (value.length / 2)
Math.ceil(value.length * 0.5)
}]
)
);

View File

@ -5,7 +5,7 @@
import { DerivedCollectiveProposal } from '@polkadot/api-derive/types';
import { ProposalIndex } from '@polkadot/types/interfaces';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { Button, Dropdown, InputAddress, Modal, TxButton } from '@polkadot/react-components';
import { useApi, useCall, useToggle } from '@polkadot/react-hooks';
@ -21,21 +21,25 @@ export default function Submission ({ councilProposals, id, isDisabled }: Props)
const { t } = useTranslation();
const { api } = useApi();
const councilThreshold = useCall<number>(api.query.electionsPhragmen?.members || api.query.elections.members, [], {
transform: (value: any[]): number => (value.length / 2) + 1
transform: (value: any[]): number =>
Math.ceil(value.length * 0.5)
});
const [isOpen, toggleOpen] = useToggle();
const [accountId, setAccountId] = useState<string | null>(null);
const [councilType, setCouncilType] = useState('reject');
const [hasProposals, setHasProposals] = useState(true);
const councilTypeOpt = useMemo(() => [
{ value: 'accept', text: t('Acceptance proposal to council') },
{ value: 'reject', text: t('Rejection proposal to council') }
], [t]);
useEffect((): void => {
const available = councilProposals
.map(({ votes }): number =>
votes ? votes?.index.toNumber() : -1
)
.filter((index): boolean => index !== -1);
setHasProposals(!!available.length);
setHasProposals(
!!councilProposals
.map(({ votes }): number => votes ? votes?.index.toNumber() : -1)
.filter((index): boolean => index !== -1)
.length
);
}, [councilProposals]);
if (hasProposals) {
@ -61,10 +65,7 @@ export default function Submission ({ councilProposals, id, isDisabled }: Props)
help={t('The type of council proposal to submit.')}
label={t('council proposal type')}
onChange={setCouncilType}
options={[
{ value: 'accept', text: t('Acceptance proposal to council') },
{ value: 'reject', text: t('Rejection proposal to council') }
]}
options={councilTypeOpt}
value={councilType}
/>
</Modal.Content>

View File

@ -4,7 +4,7 @@
import { AppProps, BareProps } 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';
@ -19,6 +19,13 @@ interface Props extends AppProps, BareProps {}
export default function TreasuryApp ({ basePath }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const items = useMemo(() => [
{
isRoot: true,
name: 'overview',
text: t('Treasury overview')
}
], [t]);
return (
<main className='treasury--App'>
@ -26,13 +33,7 @@ export default function TreasuryApp ({ basePath }: Props): React.ReactElement<Pr
<header>
<Tabs
basePath={basePath}
items={[
{
isRoot: true,
name: 'overview',
text: t('Treasury overview')
}
]}
items={items}
/>
</header>
<Switch>

View File

@ -12,11 +12,6 @@ import { Dropdown } from '@polkadot/react-components';
import { useTranslation } from '../translate';
import Bare from './Bare';
interface Option {
text: string;
value: number;
}
function doChange (onChange?: (value: any) => void): (_: number) => void {
return function (value: number): void {
onChange && onChange({
@ -28,13 +23,13 @@ function doChange (onChange?: (value: any) => void): (_: number) => void {
export default function Vote ({ className, defaultValue: { value }, isDisabled, isError, onChange, style, withLabel }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const optAye = useMemo((): Option[] => {
const optAye = useMemo(() => {
return [
{ text: t('Nay'), value: 0 },
{ text: t('Aye'), value: -1 }
];
}, [t]);
const optConv = useMemo((): Option[] => {
const optConv = useMemo(() => {
return [
{ text: t('None'), value: 0 },
{ text: t('Locked1x'), value: 1 },