Dedupe votes display (#2507)
This commit is contained in:
parent
109c6baa72
commit
305283b09b
@ -8,11 +8,12 @@ import { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import ProposalCell from '@polkadot/app-democracy/Overview/ProposalCell';
|
||||
import { AddressMini, LinkExternal } from '@polkadot/react-components';
|
||||
import { LinkExternal } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { BlockToTime } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import Votes from './Votes';
|
||||
import Voting from './Voting';
|
||||
|
||||
interface Props {
|
||||
@ -40,7 +41,7 @@ function Motion ({ className, isMember, members, motion: { hash, proposal, votes
|
||||
imageHash={hash}
|
||||
proposal={proposal}
|
||||
/>
|
||||
<td className='number'>
|
||||
<td className='number together'>
|
||||
{formatNumber(ayes.length)}/{formatNumber(threshold)}
|
||||
</td>
|
||||
<td className='number together'>
|
||||
@ -51,24 +52,8 @@ function Motion ({ className, isMember, members, motion: { hash, proposal, votes
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
<td className='address'>
|
||||
{ayes.map((address, index): React.ReactNode => (
|
||||
<AddressMini
|
||||
key={`${index}:${address}`}
|
||||
value={address}
|
||||
withBalance={false}
|
||||
/>
|
||||
))}
|
||||
</td>
|
||||
<td className='address'>
|
||||
{nays.map((address, index): React.ReactNode => (
|
||||
<AddressMini
|
||||
key={`${index}:${address}`}
|
||||
value={address}
|
||||
withBalance={false}
|
||||
/>
|
||||
))}
|
||||
</td>
|
||||
<Votes votes={ayes} />
|
||||
<Votes votes={nays} />
|
||||
<td className='button'>
|
||||
<Voting
|
||||
hash={hash}
|
||||
|
||||
@ -34,21 +34,17 @@ function Slashing ({ className, isMember, members }: Props): React.ReactElement<
|
||||
const threshold = Math.ceil((members.length || 0) * 0.5);
|
||||
|
||||
useEffect((): void => {
|
||||
if (slashes?.length) {
|
||||
setEras(
|
||||
slashes.map(([era, slashes]): Option => ({
|
||||
text: t('era {{era}}, {{count}} slashes', {
|
||||
replace: {
|
||||
count: slashes.length,
|
||||
era: era.toNumber()
|
||||
}
|
||||
}),
|
||||
value: era.toNumber()
|
||||
}))
|
||||
);
|
||||
} else {
|
||||
setEras([]);
|
||||
}
|
||||
setEras(
|
||||
(slashes || []).map(([era, slashes]): Option => ({
|
||||
text: t('era {{era}}, {{count}} slashes', {
|
||||
replace: {
|
||||
count: slashes.length,
|
||||
era: era.toNumber()
|
||||
}
|
||||
}),
|
||||
value: era.toNumber()
|
||||
}))
|
||||
);
|
||||
}, [slashes, t]);
|
||||
|
||||
useEffect((): void => {
|
||||
|
||||
28
packages/page-council/src/Motions/Votes.tsx
Normal file
28
packages/page-council/src/Motions/Votes.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2017-2020 @polkadot/app-democracy 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 { AccountId } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import { AddressMini } from '@polkadot/react-components';
|
||||
|
||||
interface Props {
|
||||
votes: AccountId[];
|
||||
}
|
||||
|
||||
function Votes ({ votes }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<td className='address'>
|
||||
{votes.map((address): React.ReactNode => (
|
||||
<AddressMini
|
||||
key={address.toString()}
|
||||
value={address}
|
||||
withBalance={false}
|
||||
/>
|
||||
))}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Votes);
|
||||
@ -13,7 +13,7 @@ import { FormatBalance } from '@polkadot/react-query';
|
||||
import { Option } from '@polkadot/types';
|
||||
|
||||
import CandidateVoting from './CandidateVoting';
|
||||
import VoteDisplay from './VoteDisplay';
|
||||
import Votes from './Votes';
|
||||
|
||||
interface Props {
|
||||
allMembers: string[];
|
||||
@ -43,7 +43,7 @@ function Candidate ({ allMembers, isMember, ownMembers, value: { accountId, kind
|
||||
<td className='number'>
|
||||
<FormatBalance value={value} />
|
||||
</td>
|
||||
<VoteDisplay votes={votes} />
|
||||
<Votes votes={votes} />
|
||||
<td className='button'>
|
||||
<CandidateVoting
|
||||
candidateId={accountId.toString()}
|
||||
|
||||
@ -12,7 +12,7 @@ import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import DefenderVoting from './DefenderVoting';
|
||||
import VoteDisplay from './VoteDisplay';
|
||||
import Votes from './Votes';
|
||||
|
||||
interface Props extends OwnMembers {
|
||||
className?: string;
|
||||
@ -45,7 +45,7 @@ function Defender ({ className, info, isMember, ownMembers }: Props): React.Reac
|
||||
<td className='address all'>
|
||||
<AddressSmall value={info.defender} />
|
||||
</td>
|
||||
<VoteDisplay votes={votes} />
|
||||
<Votes votes={votes} />
|
||||
<td className='button'>
|
||||
<DefenderVoting
|
||||
isMember={isMember}
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
// Copyright 2017-2020 @polkadot/app-society 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 { VoteSplit, VoteType } from '../types';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { AddressMini, Expander } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
votes?: VoteType[];
|
||||
}
|
||||
|
||||
function VoteDisplay ({ className, votes }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [{ allAye, allNay, allSkeptic }, setVoteSplit] = useState<VoteSplit>({ allAye: [], allNay: [], allSkeptic: [] });
|
||||
|
||||
useEffect((): void => {
|
||||
votes && setVoteSplit({
|
||||
allAye: votes.filter(([, vote]): boolean => vote.isApprove),
|
||||
allNay: votes.filter(([, vote]): boolean => vote.isReject),
|
||||
allSkeptic: votes.filter(([, vote]): boolean => vote.isSkeptic)
|
||||
});
|
||||
}, [votes]);
|
||||
|
||||
return (
|
||||
<td className={className}>
|
||||
{allSkeptic.length !== 0 && (
|
||||
<Expander summary={t('Skeptics ({{count}})', { replace: { count: allSkeptic.length } })}>
|
||||
{allSkeptic.map(([who]): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who.toString()}
|
||||
value={who}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
)}
|
||||
{allAye.length !== 0 && (
|
||||
<Expander summary={t('Approvals ({{count}})', { replace: { count: allAye.length } })}>
|
||||
{allAye.map(([who]): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who.toString()}
|
||||
value={who}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
)}
|
||||
{allNay.length !== 0 && (
|
||||
<Expander summary={t('Rejections ({{count}})', { replace: { count: allNay.length } })}>
|
||||
{allNay.map(([who]): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who.toString()}
|
||||
value={who}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
)}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(styled(VoteDisplay)`
|
||||
.ui--Expander+ui--Expander {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
`);
|
||||
46
packages/page-society/src/Overview/Votes.tsx
Normal file
46
packages/page-society/src/Overview/Votes.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2017-2020 @polkadot/app-society 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 { VoteSplit, VoteType } from '../types';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import VotesExpander from './VotesExpander';
|
||||
|
||||
interface Props {
|
||||
votes?: VoteType[];
|
||||
}
|
||||
|
||||
function Votes ({ votes }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [{ allAye, allNay, allSkeptic }, setVoteSplit] = useState<VoteSplit>({ allAye: [], allNay: [], allSkeptic: [] });
|
||||
|
||||
useEffect((): void => {
|
||||
votes && setVoteSplit({
|
||||
allAye: votes.filter(([, vote]): boolean => vote.isApprove),
|
||||
allNay: votes.filter(([, vote]): boolean => vote.isReject),
|
||||
allSkeptic: votes.filter(([, vote]): boolean => vote.isSkeptic)
|
||||
});
|
||||
}, [votes]);
|
||||
|
||||
return (
|
||||
<td className='start'>
|
||||
<VotesExpander
|
||||
label={t('Skeptics')}
|
||||
votes={allSkeptic}
|
||||
/>
|
||||
<VotesExpander
|
||||
label={t('Approvals')}
|
||||
votes={allAye}
|
||||
/>
|
||||
<VotesExpander
|
||||
label={t('Rejections')}
|
||||
votes={allNay}
|
||||
/>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Votes);
|
||||
32
packages/page-society/src/Overview/VotesExpander.tsx
Normal file
32
packages/page-society/src/Overview/VotesExpander.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2017-2020 @polkadot/app-society 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 { VoteType } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import { AddressMini, Expander } from '@polkadot/react-components';
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
votes: VoteType[];
|
||||
}
|
||||
|
||||
function VotesExpander ({ label, votes }: Props): React.ReactElement<Props> | null {
|
||||
if (votes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Expander summary={`${label} (${votes.length})`}>
|
||||
{votes.map(([who]): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who.toString()}
|
||||
value={who}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(VotesExpander);
|
||||
Loading…
Reference in New Issue
Block a user