From 697f7a8f85a34b97e4a4a11c1615e135048c16bc Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sun, 21 Mar 2021 13:19:42 +0100 Subject: [PATCH] Expand society bid kind displays (#4887) * Expand society bid kind displays * classes * vote column naming --- packages/page-society/src/Candidates/Bid.tsx | 21 +++++----- .../page-society/src/Candidates/BidType.tsx | 30 ++++++++++++++ packages/page-society/src/Candidates/Bids.tsx | 2 +- .../page-society/src/Candidates/Candidate.tsx | 7 ++-- .../src/Candidates/Candidates.tsx | 4 +- .../page-society/src/Overview/Defender.tsx | 2 +- .../page-society/src/Overview/Summary.tsx | 12 +++++- packages/page-society/src/Overview/index.tsx | 9 ++++- .../page-society/src/Suspended/Suspension.tsx | 15 ++++++- packages/page-society/src/Suspended/index.tsx | 39 +++++++++++++++---- packages/page-society/src/index.tsx | 28 +++++++------ packages/react-components/src/CardSummary.tsx | 2 +- packages/react-components/src/Progress.tsx | 27 +++---------- 13 files changed, 133 insertions(+), 65 deletions(-) create mode 100644 packages/page-society/src/Candidates/BidType.tsx diff --git a/packages/page-society/src/Candidates/Bid.tsx b/packages/page-society/src/Candidates/Bid.tsx index a0e26f810f..e099987b39 100644 --- a/packages/page-society/src/Candidates/Bid.tsx +++ b/packages/page-society/src/Candidates/Bid.tsx @@ -3,13 +3,14 @@ import type { Bid } from '@polkadot/types/interfaces'; -import React, { useEffect, useState } from 'react'; +import React, { useMemo } from 'react'; import { AddressSmall, TxButton } from '@polkadot/react-components'; import { useAccounts, useApi } from '@polkadot/react-hooks'; import { FormatBalance } from '@polkadot/react-query'; import { useTranslation } from '../translate'; +import BidType from './BidType'; interface Props { index: number; @@ -20,22 +21,22 @@ function BidRow ({ index, value: { kind, value, who } }: Props): React.ReactElem const { t } = useTranslation(); const { api } = useApi(); const { allAccounts } = useAccounts(); - const [isBidder, setIsBidder] = useState(false); - useEffect((): void => { - const address = who.toString(); + const isBidder = useMemo( + (): boolean => { + const address = who.toString(); - setIsBidder(allAccounts.some((accountId) => accountId === address)); - }, [allAccounts, who]); + return allAccounts.some((accountId) => accountId === address); + }, + [allAccounts, who] + ); return ( - + - - {kind.type} - + diff --git a/packages/page-society/src/Candidates/BidType.tsx b/packages/page-society/src/Candidates/BidType.tsx new file mode 100644 index 0000000000..48efed7ebd --- /dev/null +++ b/packages/page-society/src/Candidates/BidType.tsx @@ -0,0 +1,30 @@ +// Copyright 2017-2021 @polkadot/app-society authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { BidKind } from '@polkadot/types/interfaces'; + +import React, { useMemo } from 'react'; + +import { AddressSmall } from '@polkadot/react-components'; + +interface Props { + value?: BidKind; +} + +function BidType ({ value }: Props): React.ReactElement { + const vouchId = useMemo( + () => value?.isVouch + ? value.asVouch[0] + : null, + [value] + ); + + return ( + <> + {value && value.type} + {vouchId && } + + ); +} + +export default React.memo(BidType); diff --git a/packages/page-society/src/Candidates/Bids.tsx b/packages/page-society/src/Candidates/Bids.tsx index d75f3a3e88..f58ee0b57d 100644 --- a/packages/page-society/src/Candidates/Bids.tsx +++ b/packages/page-society/src/Candidates/Bids.tsx @@ -22,7 +22,7 @@ function Bids ({ className }: Props): React.ReactElement { const headerRef = useRef([ [t('bids'), 'start'], - [t('kind')], + [t('bid kind'), 'start', 2], [t('value')], [] ]); diff --git a/packages/page-society/src/Candidates/Candidate.tsx b/packages/page-society/src/Candidates/Candidate.tsx index f5d8abcf13..93c0b4678b 100644 --- a/packages/page-society/src/Candidates/Candidate.tsx +++ b/packages/page-society/src/Candidates/Candidate.tsx @@ -13,6 +13,7 @@ import { useApi, useCall } from '@polkadot/react-hooks'; import { FormatBalance } from '@polkadot/react-query'; import Votes from '../Overview/Votes'; +import BidType from './BidType'; import CandidateVoting from './CandidateVoting'; interface Props { @@ -28,7 +29,7 @@ function Candidate ({ allMembers, isMember, ownMembers, value: { accountId, kind transform: (voteOpts: Option[]): VoteType[] => voteOpts .map((voteOpt, index): [string, Option] => [allMembers[index], voteOpt]) - .filter(([, voteOpt]): boolean => voteOpt.isSome) + .filter(([, voteOpt]) => voteOpt.isSome) .map(([accountId, voteOpt]): VoteType => [accountId, voteOpt.unwrap()]) }); @@ -37,9 +38,7 @@ function Candidate ({ allMembers, isMember, ownMembers, value: { accountId, kind - - {kind.type} - + diff --git a/packages/page-society/src/Candidates/Candidates.tsx b/packages/page-society/src/Candidates/Candidates.tsx index d7bfde78f4..9d6d946685 100644 --- a/packages/page-society/src/Candidates/Candidates.tsx +++ b/packages/page-society/src/Candidates/Candidates.tsx @@ -23,9 +23,9 @@ function Candidates ({ allMembers, candidates, className = '', isMember, ownMemb const headerRef = useRef([ [t('candidates'), 'start'], - [t('kind')], + [t('bid kind'), 'start', 2], [t('value')], - [t('votes'), 'expand'], + [undefined, 'expand'], [] ]); diff --git a/packages/page-society/src/Overview/Defender.tsx b/packages/page-society/src/Overview/Defender.tsx index 29bc968fd0..35aaf807ea 100644 --- a/packages/page-society/src/Overview/Defender.tsx +++ b/packages/page-society/src/Overview/Defender.tsx @@ -35,7 +35,7 @@ function Defender ({ className = '', info, isMember, ownMembers }: Props): React const headerRef = useRef([ [t('defender'), 'start'], - [t('votes'), 'expand'], + [undefined, 'expand'], [] ]); diff --git a/packages/page-society/src/Overview/Summary.tsx b/packages/page-society/src/Overview/Summary.tsx index e7cbff39ac..5071fb0a7e 100644 --- a/packages/page-society/src/Overview/Summary.tsx +++ b/packages/page-society/src/Overview/Summary.tsx @@ -1,6 +1,7 @@ // Copyright 2017-2021 @polkadot/app-society authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type BN from 'bn.js'; import type { DeriveSociety } from '@polkadot/api-derive/types'; import type { BlockNumber } from '@polkadot/types/interfaces'; @@ -16,9 +17,10 @@ import { useTranslation } from '../translate'; interface Props { className?: string; info?: DeriveSociety; + payoutTotal?: BN; } -function Summary ({ className = '', info }: Props): React.ReactElement { +function Summary ({ className = '', info, payoutTotal }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const members = useCall(api.derive.society.members); @@ -65,6 +67,14 @@ function Summary ({ className = '', info }: Props): React.ReactElement { )}
+ {payoutTotal && ( + ('payouts')}> + + + )} {pot && ( ('pot')}> { +function Overview ({ className, info, isMember, mapMembers, ownMembers, payoutTotal }: Props): React.ReactElement { return (
- + { +function Suspension ({ balance, bid, value }: Props): React.ReactElement { return ( + + + {balance && ( + + )} + ); } diff --git a/packages/page-society/src/Suspended/index.tsx b/packages/page-society/src/Suspended/index.tsx index 1c88947de9..5602bb9df2 100644 --- a/packages/page-society/src/Suspended/index.tsx +++ b/packages/page-society/src/Suspended/index.tsx @@ -1,8 +1,9 @@ // Copyright 2017-2021 @polkadot/app-society authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { StorageKey } from '@polkadot/types'; -import type { AccountId } from '@polkadot/types/interfaces'; +import type { Option, StorageKey } from '@polkadot/types'; +import type { AccountId, BalanceOf, BidKind } from '@polkadot/types/interfaces'; +import type { ITuple } from '@polkadot/types/types'; import React, { useRef } from 'react'; @@ -16,7 +17,25 @@ interface Props { className?: string; } -const optExtract = { +interface CandidateSuspend { + accountId: AccountId; + balance: BalanceOf; + bid: BidKind; +} + +const optExtractCandidates = { + transform: (entries: [StorageKey<[AccountId]>, Option>][]): CandidateSuspend[] => + entries + .filter(([{ args: [accountId] }, opt]) => opt.isSome && accountId) + .map(([{ args: [accountId] }, opt]) => { + const [balance, bid] = opt.unwrap(); + + return { accountId, balance, bid }; + }) + .sort((a, b) => a.balance.cmp(b.balance)) +}; + +const optExtractAccounts = { transform: (keys: StorageKey<[AccountId]>[]): AccountId[] => keys .map(({ args: [accountId] }) => accountId) @@ -26,12 +45,14 @@ const optExtract = { function Suspended ({ className }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); - const candidates = useCall(api.query.society.suspendedCandidates.keys, undefined, optExtract); - const members = useCall(api.query.society.suspendedMembers.keys, undefined, optExtract); + const candidates = useCall(api.query.society.suspendedCandidates.entries, undefined, optExtractCandidates); + const members = useCall(api.query.society.suspendedMembers.keys, undefined, optExtractAccounts); const headerRef = useRef({ candidates: [ - [t('candidates'), 'start'] + [t('candidates'), 'start'], + [t('bid kind'), 'start', 2], + [t('value')] ], members: [ [t('members'), 'start'] @@ -45,7 +66,7 @@ function Suspended ({ className }: Props): React.ReactElement { empty={members && t('No suspended members')} header={headerRef.current.members} > - {members && members.map((accountId): React.ReactNode => ( + {members?.map((accountId): React.ReactNode => ( { empty={candidates && t('No suspended candidates')} header={headerRef.current.candidates} > - {candidates && candidates.map((accountId): React.ReactNode => ( + {candidates?.map(({ accountId, balance, bid }): React.ReactNode => ( diff --git a/packages/page-society/src/index.tsx b/packages/page-society/src/index.tsx index 0198ca8127..a183298db5 100644 --- a/packages/page-society/src/index.tsx +++ b/packages/page-society/src/index.tsx @@ -1,10 +1,10 @@ // Copyright 2017-2021 @polkadot/app-society authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type BN from 'bn.js'; import type { DeriveSociety, DeriveSocietyMember } from '@polkadot/api-derive/types'; import type { MapMember } from './types'; +import BN from 'bn.js'; import React, { useMemo } from 'react'; import { Route, Switch } from 'react-router'; @@ -72,8 +72,8 @@ function finalSort (a: MapMember, b: MapMember): number { : skepticSort(a, b); } -function getMapMembers (members: DeriveSocietyMember[], skeptics: string[], voters: string[], info: DeriveSociety, warnStrikes: BN): MapMember[] { - return members +function getMapMembers (members: DeriveSocietyMember[], skeptics: string[], voters: string[], info: DeriveSociety, warnStrikes: BN): [MapMember[], BN] { + const mapMembers = members .filter((member) => !info.hasDefender || !member.accountId.eq(info.defender)) .map((member): MapMember => ({ isCandidateVoter: voters.includes(member.accountId.toString()), @@ -87,6 +87,13 @@ function getMapMembers (members: DeriveSocietyMember[], skeptics: string[], vote .sort(({ member: a }, { member: b }) => (b.payouts.length - a.payouts.length) || b.strikes.cmp(a.strikes)) .sort(voterSort) .sort(finalSort); + + return [ + mapMembers, + mapMembers.reduce((total, { member: { payouts } }) => + payouts.reduce((total, [, balance]) => total.iadd(balance), total), new BN(0) + ) + ]; } function SocietyApp ({ basePath, className }: Props): React.ReactElement { @@ -98,10 +105,10 @@ function SocietyApp ({ basePath, className }: Props): React.ReactElement const members = useCall(api.derive.society.members); const { candidates, skeptics, voters } = useVoters(); - const mapMembers = useMemo( + const [mapMembers, payoutTotal] = useMemo( () => members && info && skeptics && voters ? getMapMembers(members, skeptics, voters, info, api.consts.society.maxStrikes.mul(BN_TWO).div(BN_THREE)) - : undefined, + : [undefined, undefined], [api, info, members, skeptics, voters] ); @@ -124,12 +131,10 @@ function SocietyApp ({ basePath, className }: Props): React.ReactElement return (
-
- -
+ isMember={isMember} mapMembers={mapMembers} ownMembers={ownMembers} + payoutTotal={payoutTotal} /> diff --git a/packages/react-components/src/CardSummary.tsx b/packages/react-components/src/CardSummary.tsx index 24533d4cf0..76e1477a61 100644 --- a/packages/react-components/src/CardSummary.tsx +++ b/packages/react-components/src/CardSummary.tsx @@ -122,7 +122,7 @@ export default React.memo(styled(CardSummary)` text-align: right; > * { - margin: 0.5rem 0; + margin: 0.25rem 0; &:first-child { margin-top: 0; diff --git a/packages/react-components/src/Progress.tsx b/packages/react-components/src/Progress.tsx index 3d5adad097..b7aa0479ed 100644 --- a/packages/react-components/src/Progress.tsx +++ b/packages/react-components/src/Progress.tsx @@ -12,7 +12,6 @@ import { bnToBn } from '@polkadot/util'; interface Props { className?: string; isDisabled?: boolean; - size?: 'normal' | 'small' total?: UInt | BN | number | null; value?: UInt | BN | number | null; } @@ -35,7 +34,7 @@ function DivClip ({ angle, type }: RotateProps): React.ReactElement const Clip = React.memo(DivClip); -function Progress ({ className = '', isDisabled, size = 'normal', total, value }: Props): React.ReactElement | null { +function Progress ({ className = '', isDisabled, total, value }: Props): React.ReactElement | null { const _total = bnToBn(total || 0); const angle = _total.gtn(0) ? (bnToBn(value || 0).muln(36000).div(_total).toNumber() / 100) @@ -48,7 +47,7 @@ function Progress ({ className = '', isDisabled, size = 'normal', total, value } const drawAngle = angle % 360; return ( -
+