useMemo for table headers/footers/filters (#2601)

* useMemo for table headers/footers/filters

* contacts mishap
This commit is contained in:
Jaco Greeff 2020-04-22 14:44:54 +02:00 committed by GitHub
parent 4939cbd75c
commit 284f7a667d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 362 additions and 288 deletions

View File

@ -7,7 +7,7 @@ import { ComponentProps as Props } from '../types';
import { SortedAccount } from './types';
import BN from 'bn.js';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import keyring from '@polkadot/ui-keyring';
import { getLedger, isLedger } from '@polkadot/react-api';
@ -98,7 +98,7 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS);
const [{ balanceTotal }, setBalances] = useState<Balances>({ accounts: {} });
const [sortedAccounts, setSortedAccounts] = useState<SortedAccount[]>([]);
const [filter, setFilter] = useState<string>('');
const [filterOn, setFilter] = useState<string>('');
useEffect((): void => {
setSortedAccounts(
@ -119,6 +119,38 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
[]
);
const header = useMemo(() => [
[t('accounts'), 'start', 3],
[t('parent'), 'address'],
[t('type')],
[t('tags'), 'start'],
[t('transactions')],
[t('balances')],
[undefined, undefined, 2]
], [t]);
const footer = useMemo(() => (
<tr>
<td colSpan={7} />
<td className='number'>
{balanceTotal && <FormatBalance value={balanceTotal} />}
</td>
<td colSpan={2} />
</tr>
), [balanceTotal]);
const filter = useMemo(() => (
<div className='filter--tags'>
<Input
autoFocus
isFull
label={t('filter by name or tags')}
onChange={setFilter}
value={filterOn}
/>
</div>
), [filterOn, t]);
return (
<div className={className}>
<Banner />
@ -171,40 +203,14 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
</Button.Group>
<Table
empty={t('no accounts yet, create or import an existing')}
filter={
<div className='filter--tags'>
<Input
autoFocus
isFull
label={t('filter by name or tags')}
onChange={setFilter}
value={filter}
/>
</div>
}
footer={
<tr>
<td colSpan={7} />
<td className='number'>
{balanceTotal && <FormatBalance value={balanceTotal} />}
</td>
<td colSpan={2} />
</tr>
}
header={[
[t('accounts'), 'start', 3],
[t('parent'), 'address'],
[t('type')],
[t('tags'), 'start'],
[t('transactions')],
[t('balances')],
[undefined, undefined, 2]
]}
filter={filter}
footer={footer}
header={header}
>
{sortedAccounts.map(({ account, isFavorite }): React.ReactNode => (
<Account
account={account}
filter={filter}
filter={filterOn}
isFavorite={isFavorite}
key={account.address}
setBalance={_setBalance}

View File

@ -4,7 +4,7 @@
import { ComponentProps as Props } from '../types';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { Button, Input, Table } from '@polkadot/react-components';
import { useAddresses, useFavorites, useToggle } from '@polkadot/react-hooks';
@ -23,7 +23,7 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
const [isCreateOpen, toggleCreate] = useToggle(false);
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS);
const [sortedAddresses, setSortedAddresses] = useState<SortedAddress[]>([]);
const [filter, setFilter] = useState<string>('');
const [filterOn, setFilter] = useState<string>('');
useEffect((): void => {
setSortedAddresses(
@ -39,6 +39,26 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
);
}, [allAddresses, favorites]);
const header = useMemo(() => [
[t('contacts'), 'start', 2],
[t('tags'), 'start'],
[t('transactions')],
[t('balances')],
[undefined, undefined, 2]
], [t]);
const filter = useMemo(() => (
<div className='filter--tags'>
<Input
autoFocus
isFull
label={t('filter by name or tags')}
onChange={setFilter}
value={filterOn}
/>
</div>
), [filterOn, t]);
return (
<div className={className}>
<Button.Group>
@ -56,29 +76,13 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
)}
<Table
empty={t('no addresses saved yet, add any existing address')}
filter={
<div className='filter--tags'>
<Input
autoFocus
isFull
label={t('filter by name or tags')}
onChange={setFilter}
value={filter}
/>
</div>
}
header={[
[t('contacts'), 'start', 2],
[t('tags'), 'start'],
[t('transactions')],
[t('balances')],
[undefined, undefined, 2]
]}
filter={filter}
header={header}
>
{sortedAddresses.map(({ address, isFavorite }): React.ReactNode => (
<Address
address={address}
filter={filter}
filter={filterOn}
isFavorite={isFavorite}
key={address}
toggleFavorite={toggleFavorite}

View File

@ -6,7 +6,7 @@ import { KeypairType } from '@polkadot/util-crypto/types';
import { GeneratorMatches, GeneratorMatch, GeneratorResult } from '@polkadot/vanitygen/types';
import { ComponentProps as Props } from '../types';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
import { Button, Dropdown, Input, Table } from '@polkadot/react-components';
import { useIsMountedRef } from '@polkadot/react-hooks';
@ -163,6 +163,19 @@ function VanityApp ({ className, onStatusChange }: Props): React.ReactElement<Pr
}
}, [_executeGeneration, isRunning]);
const header = useMemo(() => [
[t('matches'), 'start', 2],
[t('Evaluated {{count}} keys in {{elapsed}}s ({{avg}} keys/s)', {
replace: {
avg: (keyCount / (elapsed / 1000)).toFixed(3),
count: keyCount,
elapsed: (elapsed / 1000).toFixed(2)
}
}), 'start'],
[t('secret'), 'start'],
[]
], [elapsed, keyCount, t]);
return (
<div className={className}>
<div className='ui--row'>
@ -217,18 +230,7 @@ function VanityApp ({ className, onStatusChange }: Props): React.ReactElement<Pr
<Table
className='vanity--App-matches'
empty={t('No matches found')}
header={[
[t('matches'), 'start', 2],
[t('Evaluated {{count}} keys in {{elapsed}}s ({{avg}} keys/s)', {
replace: {
avg: (keyCount / (elapsed / 1000)).toFixed(3),
count: keyCount,
elapsed: (elapsed / 1000).toFixed(2)
}
}), 'start'],
[t('secret'), 'start'],
[]
]}
header={header}
>
{matches.map((match): React.ReactNode => (
<Match

View File

@ -5,7 +5,7 @@
import { DeriveCollectiveProposals, DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import { AccountId } from '@polkadot/types/interfaces';
import React from 'react';
import React, { useMemo } from 'react';
import { Button, Table } from '@polkadot/react-components';
import { useMembers } from '@polkadot/react-hooks';
@ -25,6 +25,15 @@ function Proposals ({ className, motions, prime }: Props): React.ReactElement<Pr
const { t } = useTranslation();
const { isMember, members } = useMembers();
const header = useMemo(() => [
[t('motions'), 'start', 2],
[t('threshold')],
[t('voting end')],
[t('aye'), 'address'],
[t('nay'), 'address'],
[undefined, undefined, 2]
], [t]);
return (
<div className={className}>
<Button.Group>
@ -45,14 +54,7 @@ function Proposals ({ className, motions, prime }: Props): React.ReactElement<Pr
</Button.Group>
<Table
empty={motions && t('No council motions')}
header={[
[t('motions'), 'start', 2],
[t('threshold')],
[t('voting end')],
[t('aye'), 'address'],
[t('nay'), 'address'],
[undefined, undefined, 2]
]}
header={header}
>
{motions?.map((motion: DeriveCollectiveProposal): React.ReactNode => (
<Motion

View File

@ -5,7 +5,7 @@
import { AccountId } from '@polkadot/types/interfaces';
import { ComponentProps } from './types';
import React from 'react';
import React, { useMemo } from 'react';
import { Table } from '@polkadot/react-components';
import { useTranslation } from '../translate';
@ -19,14 +19,21 @@ interface Props extends ComponentProps {
function Candidates ({ allVotes = {}, electionsInfo }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const headerRunners = useMemo(() => [
[t('runners up'), 'start', 2],
[t('backing')]
], [t]);
const headerCandidates = useMemo(() => [
[t('candidates'), 'start', 2],
[t('backing')]
], [t]);
return (
<>
<Table
empty={electionsInfo && t('No runners up found')}
header={[
[t('runners up'), 'start', 2],
[t('backing')]
]}
header={headerRunners}
>
{electionsInfo?.runnersUp.map(([accountId, balance]): React.ReactNode => (
<Candidate
@ -39,10 +46,7 @@ function Candidates ({ allVotes = {}, electionsInfo }: Props): React.ReactElemen
</Table>
<Table
empty={electionsInfo && t('No candidates found')}
header={[
[t('candidates'), 'start', 2],
[t('backing')]
]}
header={headerCandidates}
>
{electionsInfo?.candidates.map((accountId): React.ReactNode => (
<Candidate

View File

@ -5,7 +5,7 @@
import { AccountId } from '@polkadot/types/interfaces';
import { ComponentProps } from './types';
import React from 'react';
import React, { useMemo } from 'react';
import { Table } from '@polkadot/react-components';
import { useTranslation } from '../translate';
@ -20,14 +20,16 @@ interface Props extends ComponentProps {
function Members ({ allVotes = {}, className, electionsInfo, prime }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const header = useMemo(() => [
[t('members'), 'start', 2],
[t('backing')]
], [t]);
return (
<Table
className={className}
empty={electionsInfo && t('No members found')}
header={[
[t('members'), 'start', 2],
[t('backing')]
]}
header={header}
>
{electionsInfo?.members.map(([accountId, balance]): React.ReactNode => (
<Candidate

View File

@ -4,7 +4,7 @@
import { DeriveDispatch } from '@polkadot/api-derive/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
@ -20,15 +20,17 @@ function DispatchQueue ({ className }: Props): React.ReactElement<Props> | null
const { api } = useApi();
const queued = useCall<DeriveDispatch[]>(api.derive.democracy.dispatchQueue, []);
const header = useMemo(() => [
[t('dispatch queue'), 'start', 2],
[t('enact')],
[undefined, undefined, 2]
], [t]);
return (
<Table
className={className}
empty={queued && t('Nothing queued for execution')}
header={[
[t('dispatch queue'), 'start', 2],
[t('enact')],
[undefined, undefined, 2]
]}
header={header}
>
{queued?.map((entry): React.ReactNode => (
<DispatchEntry

View File

@ -4,7 +4,7 @@
import { DeriveProposalExternal } from '@polkadot/api-derive/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
@ -20,16 +20,18 @@ function Externals ({ className }: Props): React.ReactElement<Props> | null {
const { api } = useApi();
const external = useCall<DeriveProposalExternal | null>(api.derive.democracy.nextExternal, []);
const header = useMemo(() => [
[t('external'), 'start'],
[t('proposer'), 'address'],
[t('locked')],
[]
], [t]);
return (
<Table
className={className}
empty={external === null && t('No external proposal')}
header={[
[t('external'), 'start'],
[t('proposer'), 'address'],
[t('locked')],
[]
]}
header={header}
>
{external && <External value={external} />}
</Table>

View File

@ -4,7 +4,7 @@
import { DeriveProposal } from '@polkadot/api-derive/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
@ -20,16 +20,18 @@ function Proposals ({ className }: Props): React.ReactElement<Props> {
const { api } = useApi();
const proposals = useCall<DeriveProposal[]>(api.derive.democracy.proposals, []);
const header = useMemo(() => [
[t('proposals'), 'start', 2],
[t('proposer'), 'address'],
[t('locked')],
[undefined, undefined, 3]
], [t]);
return (
<Table
className={className}
empty={proposals && t('No active proposals')}
header={[
[t('proposals'), 'start', 2],
[t('proposer'), 'address'],
[t('locked')],
[undefined, undefined, 3]
]}
header={header}
>
{proposals?.map((proposal): React.ReactNode => (
<ProposalDisplay

View File

@ -4,7 +4,7 @@
import { DeriveReferendumExt } from '@polkadot/api-derive/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
@ -20,18 +20,20 @@ function Referendums ({ className }: Props): React.ReactElement<Props> {
const { api } = useApi();
const referendums = useCall<DeriveReferendumExt[]>(api.derive.democracy.referendums, []);
const header = useMemo(() => [
[t('referenda'), 'start', 2],
[t('remaining')],
[t('activate')],
[t('aye')],
[t('nay')],
[undefined, undefined, 3]
], [t]);
return (
<Table
className={className}
empty={referendums && t('No active referendums')}
header={[
[t('referenda'), 'start', 2],
[t('remaining')],
[t('activate')],
[t('aye')],
[t('nay')],
[undefined, undefined, 3]
]}
header={header}
>
{referendums?.map((referendum): React.ReactNode => (
<Referendum

View File

@ -2,7 +2,7 @@
// 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, { useMemo } from 'react';
import { HeaderExtended } from '@polkadot/api-derive';
import { Table } from '@polkadot/react-components';
@ -16,12 +16,14 @@ interface Props {
function BlockHeaders ({ headers }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const header = useMemo(() => [
[t('recent blocks'), 'start', 3]
], [t]);
return (
<Table
empty={t('No blocks available')}
header={[
[t('recent blocks'), 'start', 3]
]}
header={header}
>
{headers
.filter((header) => !!header)

View File

@ -4,7 +4,7 @@
import { BlockNumber, EventRecord, Extrinsic } from '@polkadot/types/interfaces';
import React from 'react';
import React, { useMemo } from 'react';
import { Table } from '@polkadot/react-components';
import { useTranslation } from '../translate';
@ -21,15 +21,17 @@ interface Props {
function Extrinsics ({ blockNumber, className, events, label, value }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const header = useMemo(() => [
[label || t('extrinsics'), 'start', 2],
[t('events'), 'start', 2],
[t('signer'), 'address']
], [label, t]);
return (
<Table
className={className}
empty={t('No pending extrinsics are in the queue')}
header={[
[label || t('extrinsics'), 'start', 2],
[t('events'), 'start', 2],
[t('signer'), 'address']
]}
header={header}
isFixed
>
{value?.map((extrinsic, index): React.ReactNode =>

View File

@ -5,7 +5,7 @@
import { DigestItem } from '@polkadot/types/interfaces';
import { Codec, TypeDef } from '@polkadot/types/types';
import React from 'react';
import React, { useMemo } from 'react';
import { Struct, Tuple, Raw, Vec, getTypeDef } from '@polkadot/types';
import { Expander, Table } from '@polkadot/react-components';
import Params from '@polkadot/react-params';
@ -103,10 +103,12 @@ function Logs (props: Props): React.ReactElement<Props> | null {
const { value } = props;
const { t } = useTranslation();
const header = useMemo(() => [[t('logs'), 'start']], [t]);
return (
<Table
empty={t('No logs available')}
header={[[t('logs'), 'start']]}
header={header}
>
{value?.map((log, index) => (
<tr key={`log:${index}`}>

View File

@ -4,7 +4,7 @@
import { EventRecord } from '@polkadot/types/interfaces';
import React from 'react';
import React, { useMemo } from 'react';
import { Table } from '@polkadot/react-components';
import Event from './Event';
@ -20,12 +20,14 @@ interface Props {
function Events ({ emptyLabel, eventClassName, events, label }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const header = useMemo(() => [
[label || t('recent events'), 'start']
], [label, t]);
return (
<Table
empty={emptyLabel || t('No events available')}
header={[
[label || t('recent events'), 'start']
]}
header={header}
>
{events
.filter(({ event: { method, section } }): boolean => !!method && !!section)

View File

@ -4,7 +4,7 @@
import { PeerInfo } from '@polkadot/types/interfaces';
import React from 'react';
import React, { useMemo } from 'react';
import { formatNumber } from '@polkadot/util';
import { useTranslation } from '../translate';
@ -18,16 +18,18 @@ interface Props {
function Peers ({ className, peers }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const header = useMemo(() => [
[t('connected peers'), 'start'],
[t('role'), 'start'],
[t('best #'), 'number'],
[t('best hash'), 'hash']
], [t]);
return (
<Table
className={className}
empty={t('no peers connected')}
header={[
[t('connected peers'), 'start'],
[t('role'), 'start'],
[t('best #'), 'number'],
[t('best hash'), 'hash']
]}
header={header}
>
{peers?.sort((a, b): number => b.bestNumber.cmp(a.bestNumber)).map((peer) => (
<tr key={peer.peerId.toString()}>

View File

@ -4,7 +4,7 @@
import { DeriveParachain } from '@polkadot/api-derive/types';
import React from 'react';
import React, { useMemo } from 'react';
import styled from 'styled-components';
import { Table } from '@polkadot/react-components';
@ -18,14 +18,14 @@ interface Props {
function Parachains ({ parachains }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const header = useMemo(() => [
[t('parachains'), 'start', 4],
[t('swap to id')],
[t('scheduling')]
], [t]);
return (
<Table
header={[
[t('parachains'), 'start', 4],
[t('swap to id')],
[t('scheduling')]
]}
>
<Table header={header}>
{parachains.map((parachain): React.ReactNode => (
<Parachain
key={parachain.id.toString()}

View File

@ -4,7 +4,7 @@
import { Bid } from '@polkadot/types/interfaces';
import React from 'react';
import React, { useMemo } from 'react';
import { Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
@ -20,15 +20,17 @@ function Bids ({ className }: Props): React.ReactElement<Props> {
const { api } = useApi();
const bids = useCall<Bid[]>(api.query.society.bids, []);
const header = useMemo(() => [
[t('bids'), 'start'],
[t('kind')],
[t('value')]
], [t]);
return (
<Table
className={className}
empty={bids && t('No bids')}
header={[
[t('bids'), 'start'],
[t('kind')],
[t('value')]
]}
header={header}
>
{bids?.map((bid): React.ReactNode => (
<BidRow

View File

@ -5,7 +5,7 @@
import { DeriveSocietyCandidate } from '@polkadot/api-derive/types';
import { OwnMembers } from '../types';
import React from 'react';
import React, { useMemo } from 'react';
import { Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
@ -21,16 +21,18 @@ function Candidates ({ allMembers, className, isMember, ownMembers }: Props): Re
const { api } = useApi();
const candidates = useCall<DeriveSocietyCandidate[]>(api.derive.society.candidates, []);
const header = useMemo(() => [
[t('candidates'), 'start'],
[t('kind')],
[t('value')],
[t('votes'), 'start']
], [t]);
return (
<Table
className={className}
empty={candidates && t('No candidates')}
header={[
[t('candidates'), 'start'],
[t('kind')],
[t('value')],
[t('votes'), 'start']
]}
header={header}
>
{candidates?.map((candidate): React.ReactNode => (
<Candidate

View File

@ -6,7 +6,7 @@ import { DeriveSociety, DeriveSocietyMember } from '@polkadot/api-derive/types';
import { SocietyVote } from '@polkadot/types/interfaces';
import { OwnMembers, VoteType } from '../types';
import React from 'react';
import React, { useMemo } from 'react';
import { AddressSmall, Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
@ -29,6 +29,12 @@ function Defender ({ className, info, isMember, ownMembers }: Props): React.Reac
.map(({ accountId, vote }): VoteType => [accountId.toString(), vote as SocietyVote])
});
const header = useMemo(() => [
[t('defender'), 'start'],
[t('votes'), 'start'],
[]
], [t]);
if (!info || !info.hasDefender || !info.defender) {
return null;
}
@ -36,11 +42,7 @@ function Defender ({ className, info, isMember, ownMembers }: Props): React.Reac
return (
<Table
className={className}
header={[
[t('defender'), 'start'],
[t('votes'), 'start'],
[]
]}
header={header}
>
<tr>
<td className='address all'>

View File

@ -4,7 +4,7 @@
import { DeriveSociety, DeriveSocietyMember } from '@polkadot/api-derive/types';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
@ -28,14 +28,16 @@ function Members ({ className, info }: Props): React.ReactElement<Props> {
);
}, [info, members]);
const header = useMemo(() => [
[t('members'), 'start', 3],
[t('strikes')]
], [t]);
return (
<Table
className={className}
empty={info && t('No active members')}
header={[
[t('members'), 'start', 3],
[t('strikes')]
]}
header={header}
>
{filtered.map((member): React.ReactNode => (
<Member

View File

@ -5,7 +5,7 @@
import { ActiveEraInfo, EraIndex } from '@polkadot/types/interfaces';
import BN from 'bn.js';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Table } from '@polkadot/react-components';
import { useCall, useApi, useOwnStashes } from '@polkadot/react-hooks';
import { FormatBalance } from '@polkadot/react-query';
@ -72,28 +72,32 @@ function Actions ({ allStashes, className, isInElection, next, validators }: Pro
[]
);
const header = useMemo(() => [
[t('stashes'), 'start'],
[t('controller'), 'address'],
[t('rewards'), 'number'],
[t('bonded'), 'number'],
[undefined, undefined, 2]
], [t]);
const footer = useMemo(() => (
<tr>
<td colSpan={3} />
<td className='number'>
{bondedTotal && <FormatBalance value={bondedTotal} />}
</td>
<td colSpan={2} />
</tr>
), [bondedTotal]);
return (
<div className={className}>
<NewStake />
<ElectionBanner isInElection={isInElection} />
<Table
empty={t('No funds staked yet. Bond funds to validate or nominate a validator')}
footer={
<tr>
<td colSpan={3} />
<td className='number'>
{bondedTotal && <FormatBalance value={bondedTotal} />}
</td>
<td colSpan={2} />
</tr>
}
header={[
[t('stashes'), 'start'],
[t('controller'), 'address'],
[t('rewards'), 'number'],
[t('bonded'), 'number'],
[undefined, undefined, 2]
]}
footer={footer}
header={header}
>
{foundStashes?.map(([stashId, isOwnStash]): React.ReactNode => (
<Account

View File

@ -6,7 +6,7 @@ import { DeriveHeartbeats, DeriveStakingOverview } from '@polkadot/api-derive/ty
import { AccountId, Nominations } from '@polkadot/types/interfaces';
import { Authors } from '@polkadot/react-query/BlockAuthors';
import React, { useCallback, useContext, useEffect, useState } from 'react';
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { Input, Table } from '@polkadot/react-components';
import { useApi, useCall, useFavorites } from '@polkadot/react-hooks';
import { BlockAuthorsContext } from '@polkadot/react-query';
@ -111,6 +111,33 @@ function CurrentList ({ hasQueries, isIntentions, next, setNominators, stakingOv
);
}, [nominators]);
const headerActive = useMemo(() => [
[t('intentions'), 'start', 3],
[t('nominators'), 'start', 2],
[t('commission'), 'number', 1],
[undefined, undefined, 3]
], [t]);
const headerWaiting = useMemo(() => [
[t('validators'), 'start', 3],
[t('other stake')],
[t('own stake')],
[t('commission')],
[t('points')],
[t('last #')],
[]
], [t]);
const filter = useMemo(() => (
<Input
autoFocus
isFull
label={t('filter by name, address or index')}
onChange={setNameFilter}
value={nameFilter}
/>
), [nameFilter, t]);
const _renderRows = useCallback(
(addresses?: AccountExtend[], isMain?: boolean): React.ReactNode[] =>
(addresses || []).map(([address, isElected, isFavorite]): React.ReactNode => (
@ -139,12 +166,7 @@ function CurrentList ({ hasQueries, isIntentions, next, setNominators, stakingOv
? (
<Table
empty={waiting && t('No waiting validators found')}
header={[
[t('intentions'), 'start', 3],
[t('nominators'), 'start', 2],
[t('commission'), 'number', 1],
[undefined, undefined, 3]
]}
header={headerActive}
>
{_renderRows(elected, false).concat(..._renderRows(waiting, false))}
</Table>
@ -152,24 +174,8 @@ function CurrentList ({ hasQueries, isIntentions, next, setNominators, stakingOv
: (
<Table
empty={validators && t('No active validators found')}
filter={
<Input
autoFocus
isFull
label={t('filter by name, address or index')}
onChange={setNameFilter}
value={nameFilter}
/>
}
header={[
[t('validators'), 'start', 3],
[t('other stake')],
[t('own stake')],
[t('commission')],
[t('points')],
[t('last #')],
[]
]}
filter={filter}
header={headerWaiting}
>
{_renderRows(validators, true)}
</Table>

View File

@ -6,7 +6,7 @@ import { DeriveStakerReward } from '@polkadot/api-derive/types';
import { PayoutStash, PayoutValidator } from './types';
import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { Button, Table } from '@polkadot/react-components';
import { useApi, useOwnEraRewards } from '@polkadot/react-hooks';
@ -109,6 +109,32 @@ function Payouts ({ className, isInElection }: Props): React.ReactElement<Props>
}
}, [allRewards]);
const headerStashes = useMemo(() => [
[t('payout/stash'), 'start'],
[t('eras'), 'start'],
[t('available')],
[('remaining')],
[undefined, undefined, 3]
], [t]);
const headerValidators = useMemo(() => [
[t('payout/validator'), 'start'],
[t('eras'), 'start'],
[t('available')],
[('remaining')],
[undefined, undefined, 3]
], [t]);
const footer = useMemo(() => (
<tr>
<td colSpan={2} />
<td className='number'>
{stashTotal && <FormatBalance value={stashTotal} />}
</td>
<td colSpan={4} />
</tr>
), [stashTotal]);
return (
<div className={className}>
{api.tx.staking.payoutStakers && (
@ -123,22 +149,8 @@ function Payouts ({ className, isInElection }: Props): React.ReactElement<Props>
<Table
empty={stashes && t('No pending payouts for your stashes')}
emptySpinner={t('Retrieving info for all applicable eras, this will take some time')}
footer={
<tr>
<td colSpan={2} />
<td className='number'>
{stashTotal && <FormatBalance value={stashTotal} />}
</td>
<td colSpan={4} />
</tr>
}
header={[
[t('payout/stash'), 'start'],
[t('eras'), 'start'],
[t('available')],
[('remaining')],
[undefined, undefined, 3]
]}
footer={footer}
header={headerStashes}
isFixed
>
{stashes?.map((payout): React.ReactNode => (
@ -153,13 +165,7 @@ function Payouts ({ className, isInElection }: Props): React.ReactElement<Props>
{api.tx.staking.payoutStakers && (
<Table
empty={validators && t('No pending era payouts from validators')}
header={[
[t('payout/validator'), 'start'],
[t('eras'), 'start'],
[t('available')],
[('remaining')],
[undefined, undefined, 3]
]}
header={headerValidators}
isFixed
>
{validators?.map((payout): React.ReactNode => (

View File

@ -210,6 +210,28 @@ function Targets ({ className }: Props): React.ReactElement<Props> {
}
}, [allAccounts, amount, electedInfo, favorites, lastReward, sortBy, sortFromMax]);
const header = useMemo(() => [
[t('validators'), 'start', 3],
...['rankComm', 'rankBondTotal', 'rankBondOwn', 'rankBondOther', 'rankOverall'].map((header) => [
<>{labels[header]}<Icon name={sortBy === header ? (sortFromMax ? 'chevron down' : 'chevron up') : 'minus'} /></>,
`isClickable ${sortBy === header && 'ui--highlight--border'} number`,
1,
(): void => _sort(header as 'rankComm')
]),
[]
], [_sort, labels, sortBy, sortFromMax, t]);
const filter = useMemo(() => (
<InputBalance
className='balanceInput'
help={t('The amount that will be used on a per-validator basis to calculate rewards for that validator.')}
isFull
label={t('amount to use for estimation')}
onChange={setAmount}
value={_amount}
/>
), [_amount, t]);
return (
<div className={className}>
<Summary
@ -220,26 +242,8 @@ function Targets ({ className }: Props): React.ReactElement<Props> {
/>
<Table
empty={sorted && t('No active validators to check for rewards available')}
filter={
<InputBalance
className='balanceInput'
help={t('The amount that will be used on a per-validator basis to calculate rewards for that validator.')}
isFull
label={t('amount to use for estimation')}
onChange={setAmount}
value={_amount}
/>
}
header={[
[t('validators'), 'start', 3],
...['rankComm', 'rankBondTotal', 'rankBondOwn', 'rankBondOther', 'rankOverall'].map((header) => [
<>{labels[header]}<Icon name={sortBy === header ? (sortFromMax ? 'chevron down' : 'chevron up') : 'minus'} /></>,
`isClickable ${sortBy === header && 'ui--highlight--border'} number`,
1,
(): void => _sort(header as 'rankComm')
]),
[]
]}
filter={filter}
header={header}
>
{sorted?.map((info): React.ReactNode =>
<Validator

View File

@ -4,7 +4,7 @@
import { AccountId } from '@polkadot/types/interfaces';
import React from 'react';
import React, { useMemo } from 'react';
import { AddressSmall, Table, Tag } from '@polkadot/react-components';
import { useTranslation } from '../translate';
@ -18,13 +18,15 @@ interface Props {
function Members ({ className, members, prime }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const header = useMemo(() => [
[t('members'), 'start', 3]
], [t]);
return (
<Table
className={className}
empty={members && t('No members found')}
header={[
[t('members'), 'start', 3]
]}
header={header}
>
{members?.map((accountId): React.ReactNode => (
<tr key={accountId.toString()}>

View File

@ -5,7 +5,7 @@
import { Hash } from '@polkadot/types/interfaces';
import { ComponentProps as Props } from '../types';
import React from 'react';
import React, { useMemo } from 'react';
import { Button, Table } from '@polkadot/react-components';
import { useTranslation } from '../translate';
@ -15,6 +15,14 @@ import Propose from './Propose';
function Proposals ({ className, isMember, members, prime, proposals }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const header = useMemo(() => [
[t('proposals'), 'start', 2],
[t('threshold')],
[t('aye'), 'address'],
[t('nay'), 'address'],
[]
], [t]);
return (
<div className={className}>
<Button.Group>
@ -25,13 +33,7 @@ function Proposals ({ className, isMember, members, prime, proposals }: Props):
</Button.Group>
<Table
empty={proposals && t('No committee proposals')}
header={[
[t('proposals'), 'start', 2],
[t('threshold')],
[t('aye'), 'address'],
[t('nay'), 'address'],
[]
]}
header={header}
>
{proposals?.map((hash: Hash): React.ReactNode => (
<Proposal

View File

@ -4,7 +4,7 @@
import { DeriveTreasuryProposal } from '@polkadot/api-derive/types';
import React, { useCallback } from 'react';
import React, { useCallback, useMemo } from 'react';
import { useHistory } from 'react-router-dom';
import { Table } from '@polkadot/react-components';
@ -29,17 +29,19 @@ function ProposalsBase ({ className, isApprovals, isMember, proposals }: Props):
[history]
);
const header = useMemo(() => [
[isApprovals ? t('Approved') : t('Proposals'), 'start', 2],
[t('beneficiary'), 'address'],
[t('payment')],
[t('bond')],
[undefined, undefined, 2]
], [isApprovals, t]);
return (
<Table
className={className}
empty={proposals && (isApprovals ? t('No approved proposals') : t('No pending proposals'))}
header={[
[isApprovals ? t('Approved') : t('Proposals'), 'start', 2],
[t('beneficiary'), 'address'],
[t('payment')],
[t('bond')],
[undefined, undefined, 2]
]}
header={header}
>
{proposals?.map((proposal): React.ReactNode => (
<Proposal

View File

@ -2,7 +2,7 @@
// 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, { useMemo } from 'react';
import { Table } from '@polkadot/react-components';
import { useTranslation } from '../translate';
@ -18,18 +18,20 @@ interface Props {
function Tips ({ className, hashes, isMember, members }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const header = useMemo(() => [
[t('tips'), 'start'],
[t('finder'), 'address'],
[t('fee')],
[t('reason'), 'start'],
[],
[]
], [t]);
return (
<Table
className={className}
empty={hashes && t('No open tips')}
header={[
[t('tips'), 'start'],
[t('finder'), 'address'],
[t('fee')],
[t('reason'), 'start'],
[],
[]
]}
header={header}
>
{hashes?.map((hash): React.ReactNode => (
<Tip