Expand society bid kind displays (#4887)
* Expand society bid kind displays * classes * vote column naming
This commit is contained in:
parent
1a1b6122c5
commit
697f7a8f85
@ -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 (
|
||||
<tr>
|
||||
<td className='all'>
|
||||
<td className='address all'>
|
||||
<AddressSmall value={who} />
|
||||
</td>
|
||||
<td className='number'>
|
||||
{kind.type}
|
||||
</td>
|
||||
<BidType value={kind} />
|
||||
<td className='number'>
|
||||
<FormatBalance value={value} />
|
||||
</td>
|
||||
|
||||
30
packages/page-society/src/Candidates/BidType.tsx
Normal file
30
packages/page-society/src/Candidates/BidType.tsx
Normal file
@ -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<Props> {
|
||||
const vouchId = useMemo(
|
||||
() => value?.isVouch
|
||||
? value.asVouch[0]
|
||||
: null,
|
||||
[value]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<td className='no-pad-right'>{value && value.type}</td>
|
||||
<td>{vouchId && <AddressSmall value={vouchId} />}</td>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(BidType);
|
||||
@ -22,7 +22,7 @@ function Bids ({ className }: Props): React.ReactElement<Props> {
|
||||
|
||||
const headerRef = useRef([
|
||||
[t('bids'), 'start'],
|
||||
[t('kind')],
|
||||
[t('bid kind'), 'start', 2],
|
||||
[t('value')],
|
||||
[]
|
||||
]);
|
||||
|
||||
@ -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<SocietyVote>[]): VoteType[] =>
|
||||
voteOpts
|
||||
.map((voteOpt, index): [string, Option<SocietyVote>] => [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
|
||||
<td className='address all'>
|
||||
<AddressSmall value={accountId} />
|
||||
</td>
|
||||
<td className='number'>
|
||||
{kind.type}
|
||||
</td>
|
||||
<BidType value={kind} />
|
||||
<td className='number'>
|
||||
<FormatBalance value={value} />
|
||||
</td>
|
||||
|
||||
@ -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'],
|
||||
[]
|
||||
]);
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ function Defender ({ className = '', info, isMember, ownMembers }: Props): React
|
||||
|
||||
const headerRef = useRef([
|
||||
[t('defender'), 'start'],
|
||||
[t('votes'), 'expand'],
|
||||
[undefined, 'expand'],
|
||||
[]
|
||||
]);
|
||||
|
||||
|
||||
@ -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<Props> {
|
||||
function Summary ({ className = '', info, payoutTotal }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const members = useCall<unknown[]>(api.derive.society.members);
|
||||
@ -65,6 +67,14 @@ function Summary ({ className = '', info }: Props): React.ReactElement<Props> {
|
||||
</>
|
||||
)}
|
||||
<section>
|
||||
{payoutTotal && (
|
||||
<CardSummary label={t<string>('payouts')}>
|
||||
<FormatBalance
|
||||
value={payoutTotal}
|
||||
withSi
|
||||
/>
|
||||
</CardSummary>
|
||||
)}
|
||||
{pot && (
|
||||
<CardSummary label={t<string>('pot')}>
|
||||
<FormatBalance
|
||||
|
||||
@ -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 { MapMember } from '../types';
|
||||
|
||||
@ -17,12 +18,16 @@ interface Props {
|
||||
isMember: boolean;
|
||||
mapMembers?: MapMember[];
|
||||
ownMembers: string[];
|
||||
payoutTotal?: BN;
|
||||
}
|
||||
|
||||
function Overview ({ className, info, isMember, mapMembers, ownMembers }: Props): React.ReactElement<Props> {
|
||||
function Overview ({ className, info, isMember, mapMembers, ownMembers, payoutTotal }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<div className={className}>
|
||||
<Summary info={info} />
|
||||
<Summary
|
||||
info={info}
|
||||
payoutTotal={payoutTotal}
|
||||
/>
|
||||
<Defender
|
||||
info={info}
|
||||
isMember={isMember}
|
||||
|
||||
@ -1,22 +1,33 @@
|
||||
// Copyright 2017-2021 @polkadot/app-society authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { AccountId } from '@polkadot/types/interfaces';
|
||||
import type { AccountId, BalanceOf, BidKind } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { AddressSmall } from '@polkadot/react-components';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
|
||||
import BidType from '../Candidates/BidType';
|
||||
|
||||
interface Props {
|
||||
balance?: BalanceOf;
|
||||
bid?: BidKind;
|
||||
value: AccountId;
|
||||
}
|
||||
|
||||
function Suspension ({ value }: Props): React.ReactElement<Props> {
|
||||
function Suspension ({ balance, bid, value }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<tr>
|
||||
<td className='address all'>
|
||||
<AddressSmall value={value} />
|
||||
</td>
|
||||
<BidType value={bid} />
|
||||
<td className='number'>
|
||||
{balance && (
|
||||
<FormatBalance value={balance} />
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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<ITuple<[BalanceOf, BidKind]>>][]): 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<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const candidates = useCall<AccountId[]>(api.query.society.suspendedCandidates.keys, undefined, optExtract);
|
||||
const members = useCall<AccountId[]>(api.query.society.suspendedMembers.keys, undefined, optExtract);
|
||||
const candidates = useCall<CandidateSuspend[]>(api.query.society.suspendedCandidates.entries, undefined, optExtractCandidates);
|
||||
const members = useCall<AccountId[]>(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<Props> {
|
||||
empty={members && t<string>('No suspended members')}
|
||||
header={headerRef.current.members}
|
||||
>
|
||||
{members && members.map((accountId): React.ReactNode => (
|
||||
{members?.map((accountId): React.ReactNode => (
|
||||
<Suspension
|
||||
key={accountId.toString()}
|
||||
value={accountId}
|
||||
@ -57,8 +78,10 @@ function Suspended ({ className }: Props): React.ReactElement<Props> {
|
||||
empty={candidates && t<string>('No suspended candidates')}
|
||||
header={headerRef.current.candidates}
|
||||
>
|
||||
{candidates && candidates.map((accountId): React.ReactNode => (
|
||||
{candidates?.map(({ accountId, balance, bid }): React.ReactNode => (
|
||||
<Suspension
|
||||
balance={balance}
|
||||
bid={bid}
|
||||
key={accountId.toString()}
|
||||
value={accountId}
|
||||
/>
|
||||
|
||||
@ -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<Props> {
|
||||
@ -98,10 +105,10 @@ function SocietyApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
const members = useCall<DeriveSocietyMember[]>(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<Props>
|
||||
|
||||
return (
|
||||
<main className={className}>
|
||||
<header>
|
||||
<Tabs
|
||||
basePath={basePath}
|
||||
items={items}
|
||||
/>
|
||||
</header>
|
||||
<Tabs
|
||||
basePath={basePath}
|
||||
items={items}
|
||||
/>
|
||||
<Switch>
|
||||
<Route path={`${basePath}/candidates`}>
|
||||
<Candidates
|
||||
@ -148,6 +153,7 @@ function SocietyApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
isMember={isMember}
|
||||
mapMembers={mapMembers}
|
||||
ownMembers={ownMembers}
|
||||
payoutTotal={payoutTotal}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<RotateProps>
|
||||
|
||||
const Clip = React.memo(DivClip);
|
||||
|
||||
function Progress ({ className = '', isDisabled, size = 'normal', total, value }: Props): React.ReactElement<Props> | null {
|
||||
function Progress ({ className = '', isDisabled, total, value }: Props): React.ReactElement<Props> | 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 (
|
||||
<div className={`ui--Progress${isDisabled ? ' isDisabled' : ''} ${size}Size ${className}`}>
|
||||
<div className={`ui--Progress${isDisabled ? ' isDisabled' : ''} ${className}`}>
|
||||
<div className='background highlight--bg' />
|
||||
<Clip
|
||||
angle={
|
||||
@ -76,9 +75,9 @@ function Progress ({ className = '', isDisabled, size = 'normal', total, value }
|
||||
export default React.memo(styled(Progress)`
|
||||
border-radius: 100%;
|
||||
clip-path: circle(50%);
|
||||
height: 4.5rem;
|
||||
height: 4rem;
|
||||
position: relative;
|
||||
width: 4.5rem;
|
||||
width: 4rem;
|
||||
|
||||
&.isDisabled {
|
||||
filter: grayscale(100%);
|
||||
@ -142,24 +141,8 @@ export default React.memo(styled(Progress)`
|
||||
|
||||
div {
|
||||
line-height: 1;
|
||||
font-size: 1.25rem;
|
||||
font-size: 1.1rem;
|
||||
text-shadow: 0 0 2px #f5f3f1;
|
||||
}
|
||||
}
|
||||
|
||||
&.smallSize {
|
||||
height: 2.25rem;
|
||||
width: 2.25rem;
|
||||
|
||||
.inner {
|
||||
bottom: 0.1875rem;
|
||||
left: 0.1875rem;
|
||||
right: 0.1875rem;
|
||||
top: 0.1875rem;
|
||||
}
|
||||
|
||||
div {
|
||||
font-size: 0.625rem;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user