Display collective prime voter info and warnings (#2374)
* Display prime voter info * enhancements * Update packages/page-council/src/index.tsx * Use React.memo, align society with council displays * Fix merge lints Co-authored-by: Jaco Greeff <jacogr@gmail.com>
This commit is contained in:
parent
0fa5f18ab4
commit
38405a8d3a
@ -2,9 +2,9 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useAccounts, useApi } from '@polkadot/react-hooks';
|
||||
import { useAccounts, useApi, useToggle } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import BaseOverlay from './Base';
|
||||
@ -13,16 +13,11 @@ interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function Accounts ({ className }: Props): React.ReactElement<Props> | null {
|
||||
function Accounts ({ className }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { hasAccounts } = useAccounts();
|
||||
const { isApiReady } = useApi();
|
||||
const [isHidden, setIsHidden] = useState(false);
|
||||
|
||||
const _onClose = useCallback(
|
||||
(): void => setIsHidden(true),
|
||||
[]
|
||||
);
|
||||
const [isHidden, toggleHidden] = useToggle();
|
||||
|
||||
if (!isApiReady || hasAccounts || isHidden) {
|
||||
return null;
|
||||
@ -38,7 +33,7 @@ export default function Accounts ({ className }: Props): React.ReactElement<Prop
|
||||
<p>
|
||||
<Link
|
||||
to='/accounts'
|
||||
onClick={_onClose}
|
||||
onClick={toggleHidden}
|
||||
>
|
||||
{t('Create an account now.')}
|
||||
</Link>
|
||||
@ -46,3 +41,5 @@ export default function Accounts ({ className }: Props): React.ReactElement<Prop
|
||||
</BaseOverlay>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Accounts);
|
||||
|
||||
@ -2,9 +2,10 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Icon } from '@polkadot/react-components';
|
||||
import { useToggle } from '@polkadot/react-hooks';
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
@ -14,12 +15,7 @@ interface Props {
|
||||
}
|
||||
|
||||
function BaseOverlay ({ children, className, icon, type }: Props): React.ReactElement<Props> | null {
|
||||
const [isHidden, setIsHidden] = useState(false);
|
||||
|
||||
const _onClose = useCallback(
|
||||
(): void => setIsHidden(true),
|
||||
[]
|
||||
);
|
||||
const [isHidden, toggleHidden] = useToggle();
|
||||
|
||||
if (isHidden) {
|
||||
return null;
|
||||
@ -39,7 +35,7 @@ function BaseOverlay ({ children, className, icon, type }: Props): React.ReactEl
|
||||
<Icon
|
||||
className='closeIcon'
|
||||
name='close'
|
||||
onClick={_onClose}
|
||||
onClick={toggleHidden}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -10,15 +10,15 @@ import { useTranslation } from '../translate';
|
||||
import BaseOverlay from './Base';
|
||||
|
||||
const wsUrl = settings.apiUrl;
|
||||
const isWs = wsUrl.startsWith('ws://');
|
||||
const isWsLocal = wsUrl.includes('127.0.0.1');
|
||||
const isWs = typeof wsUrl === 'string' && wsUrl.startsWith('ws://');
|
||||
const isWsLocal = typeof wsUrl === 'string' && wsUrl.includes('127.0.0.1');
|
||||
const isHttps = window.location.protocol.startsWith('https:');
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function Connecting ({ className }: Props): React.ReactElement<Props> | null {
|
||||
function Connecting ({ className }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { isApiConnected, isWaitingInjected } = useApi();
|
||||
|
||||
@ -51,3 +51,5 @@ export default function Connecting ({ className }: Props): React.ReactElement<Pr
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default React.memo(Connecting);
|
||||
|
||||
@ -2,6 +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 { AccountId } from '@polkadot/types/interfaces';
|
||||
import { DerivedCollectiveProposal } from '@polkadot/api-derive/types';
|
||||
|
||||
import React from 'react';
|
||||
@ -17,9 +18,10 @@ interface Props {
|
||||
isMember: boolean;
|
||||
members: string[];
|
||||
motion: DerivedCollectiveProposal;
|
||||
prime: AccountId | null;
|
||||
}
|
||||
|
||||
export default function Motion ({ className, isMember, members, motion: { hash, proposal, votes } }: Props): React.ReactElement<Props> | null {
|
||||
export default function Motion ({ className, isMember, members, motion: { hash, proposal, votes }, prime }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!votes) {
|
||||
@ -66,6 +68,7 @@ export default function Motion ({ className, isMember, members, motion: { hash,
|
||||
idNumber={index}
|
||||
isDisabled={!isMember}
|
||||
members={members}
|
||||
prime={prime}
|
||||
proposal={proposal}
|
||||
/>
|
||||
<LinkExternal
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Hash, Proposal, ProposalIndex } from '@polkadot/types/interfaces';
|
||||
import { AccountId, Hash, Proposal, ProposalIndex } from '@polkadot/types/interfaces';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Button, Modal, ProposedAction, VoteAccount, VoteActions, VoteToggle } from '@polkadot/react-components';
|
||||
import { Button, Icon, Modal, ProposedAction, VoteAccount, VoteActions, VoteToggle } from '@polkadot/react-components';
|
||||
import { useAccounts, useToggle } from '@polkadot/react-hooks';
|
||||
import { isBoolean } from '@polkadot/util';
|
||||
|
||||
@ -16,10 +16,11 @@ interface Props {
|
||||
idNumber: ProposalIndex;
|
||||
isDisabled: boolean;
|
||||
members: string[];
|
||||
prime: AccountId | null;
|
||||
proposal: Proposal;
|
||||
}
|
||||
|
||||
export default function Voting ({ hash, idNumber, isDisabled, members, proposal }: Props): React.ReactElement<Props> | null {
|
||||
export default function Voting ({ hash, idNumber, isDisabled, members, prime, proposal }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { hasAccounts } = useAccounts();
|
||||
const [isVotingOpen, toggleVoting] = useToggle();
|
||||
@ -31,6 +32,7 @@ export default function Voting ({ hash, idNumber, isDisabled, members, proposal
|
||||
}
|
||||
|
||||
const _onChangeVote = (vote?: boolean): void => setVoteValue(isBoolean(vote) ? vote : true);
|
||||
const isPrime = prime?.toString() === accountId;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -52,6 +54,11 @@ export default function Voting ({ hash, idNumber, isDisabled, members, proposal
|
||||
onChange={_onChangeVote}
|
||||
value={voteValue}
|
||||
/>
|
||||
{isPrime && (
|
||||
<article className='warning'>
|
||||
<div><Icon name='warning sign' />{t('You are voting with this collective\'s prime account. The vote will be the default outcome in case of any abstentions.')}</div>
|
||||
</article>
|
||||
)}
|
||||
</Modal.Content>
|
||||
<VoteActions
|
||||
accountId={accountId}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { DerivedCollectiveProposals, DerivedCollectiveProposal } from '@polkadot/api-derive/types';
|
||||
import { AccountId } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Table } from '@polkadot/react-components';
|
||||
@ -16,9 +17,10 @@ import Slashing from './Slashing';
|
||||
interface Props {
|
||||
className?: string;
|
||||
motions?: DerivedCollectiveProposals;
|
||||
prime: AccountId | null;
|
||||
}
|
||||
|
||||
export default function Proposals ({ className, motions }: Props): React.ReactElement<Props> {
|
||||
function Proposals ({ className, motions, prime }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { isMember, members } = useCouncilMembers();
|
||||
|
||||
@ -46,6 +48,7 @@ export default function Proposals ({ className, motions }: Props): React.ReactEl
|
||||
key={motion.hash.toHex()}
|
||||
members={members}
|
||||
motion={motion}
|
||||
prime={prime}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
@ -56,3 +59,5 @@ export default function Proposals ({ className, motions }: Props): React.ReactEl
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Proposals);
|
||||
|
||||
@ -5,26 +5,43 @@
|
||||
import { AccountId, Balance } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import { AddressSmall } from '@polkadot/react-components';
|
||||
import styled from 'styled-components';
|
||||
import { AddressSmall, Badge, Icon } from '@polkadot/react-components';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import Voters from './Voters';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
address: AccountId;
|
||||
balance?: Balance;
|
||||
isPrime?: boolean;
|
||||
voters?: AccountId[];
|
||||
}
|
||||
|
||||
export default function Candidate ({ address, balance, voters }: Props): React.ReactElement<Props> {
|
||||
function Candidate ({ className, address, balance, isPrime, voters }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<tr className={`${className} ${isPrime ? 'council--isPrime' : ''}`}>
|
||||
<td className='top'>
|
||||
<AddressSmall value={address} />
|
||||
</td>
|
||||
<td className='council--prime'>
|
||||
{isPrime && (
|
||||
<div>
|
||||
<Badge
|
||||
hover={t('Prime member')}
|
||||
info={<Icon name='chess king' />}
|
||||
isInline
|
||||
isTooltip
|
||||
type='green'
|
||||
/>
|
||||
<span> {t('prime voter')}</span>
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
<td className='top together right'>
|
||||
{balance && balance.gtn(0) && (
|
||||
<FormatBalance label={<label>{t('backing')}</label>} value={balance} />
|
||||
@ -38,3 +55,21 @@ export default function Candidate ({ address, balance, voters }: Props): React.R
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(styled(Candidate)`
|
||||
&.council--isPrime {
|
||||
td {
|
||||
background-color: rgba(239, 255, 239, 0.8) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.council--prime > div {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
|
||||
> span {
|
||||
color: green;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
@ -7,7 +7,7 @@ import { AccountId } from '@polkadot/types/interfaces';
|
||||
import { ComponentProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import Candidate from './Candidate';
|
||||
@ -17,46 +17,52 @@ interface Props extends ComponentProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function Candidates ({ allVotes = {}, className, electionsInfo: { candidates, runnersUp } }: Props): React.ReactElement<Props> {
|
||||
function Candidates ({ allVotes = {}, className, electionsInfo }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<h1>{t('runners up')}</h1>
|
||||
{runnersUp.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{runnersUp.map(([accountId, balance]): React.ReactNode => (
|
||||
<Candidate
|
||||
address={accountId}
|
||||
balance={balance}
|
||||
key={accountId.toString()}
|
||||
voters={allVotes[accountId.toString()]}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: t('No runners up found')
|
||||
{electionsInfo
|
||||
? electionsInfo.runnersUp.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{electionsInfo.runnersUp.map(([accountId, balance]): React.ReactNode => (
|
||||
<Candidate
|
||||
address={accountId}
|
||||
balance={balance}
|
||||
key={accountId.toString()}
|
||||
voters={allVotes[accountId.toString()]}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: t('No runners up found')
|
||||
: <Spinner />
|
||||
}
|
||||
<h1>{t('candidates')}</h1>
|
||||
{candidates.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{candidates.map((accountId): React.ReactNode => (
|
||||
<Candidate
|
||||
address={accountId}
|
||||
key={accountId.toString()}
|
||||
voters={allVotes[accountId.toString()]}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: t('No candidates found')
|
||||
{electionsInfo
|
||||
? electionsInfo.candidates.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{electionsInfo.candidates.map((accountId): React.ReactNode => (
|
||||
<Candidate
|
||||
address={accountId}
|
||||
key={accountId.toString()}
|
||||
voters={allVotes[accountId.toString()]}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: t('No candidates found')
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Candidates);
|
||||
|
||||
@ -6,7 +6,7 @@ import { AccountId } from '@polkadot/types/interfaces';
|
||||
import { ComponentProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import Candidate from './Candidate';
|
||||
@ -14,30 +14,41 @@ import Candidate from './Candidate';
|
||||
interface Props extends ComponentProps {
|
||||
allVotes?: Record<string, AccountId[]>;
|
||||
className?: string;
|
||||
prime?: AccountId | null;
|
||||
}
|
||||
|
||||
export default function Members ({ allVotes = {}, className, electionsInfo: { members } }: Props): React.ReactElement<Props> {
|
||||
function Members ({ allVotes = {}, className, electionsInfo, prime }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{members.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{members.map(([accountId, balance]): React.ReactNode => (
|
||||
<Candidate
|
||||
address={accountId}
|
||||
balance={balance}
|
||||
key={accountId.toString()}
|
||||
voters={allVotes[accountId.toString()]}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: t('No members found')
|
||||
<h1>{t('members')}</h1>
|
||||
{electionsInfo
|
||||
? electionsInfo.members.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{electionsInfo.members.map(([accountId, balance]): React.ReactNode => {
|
||||
const isPrime = prime?.toString() === accountId.toString();
|
||||
|
||||
return (
|
||||
<Candidate
|
||||
address={accountId}
|
||||
balance={balance}
|
||||
isPrime={isPrime}
|
||||
key={accountId.toString()}
|
||||
voters={allVotes[accountId.toString()]}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: t('No members found')
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Members);
|
||||
|
||||
@ -16,9 +16,15 @@ interface Props extends ComponentProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function Summary ({ bestNumber, className, electionsInfo: { members, candidateCount, desiredSeats, runnersUp, termDuration, voteCount } }: Props): React.ReactElement<Props> {
|
||||
function Summary ({ bestNumber, className, electionsInfo }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!electionsInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { members, candidateCount, desiredSeats, runnersUp, termDuration, voteCount } = electionsInfo;
|
||||
|
||||
return (
|
||||
<SummaryBox className={className}>
|
||||
<section>
|
||||
@ -54,3 +60,5 @@ export default function Summary ({ bestNumber, className, electionsInfo: { membe
|
||||
</SummaryBox>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Summary);
|
||||
|
||||
@ -64,17 +64,22 @@ class Vote extends TxModal<Props, State> {
|
||||
}
|
||||
|
||||
protected renderTrigger = (): React.ReactNode => {
|
||||
const { electionsInfo: { candidates, members, runnersUp }, t } = this.props;
|
||||
const available = members
|
||||
.map(([accountId]): AccountId => accountId)
|
||||
.concat(runnersUp.map(([accountId]): AccountId => accountId))
|
||||
.concat(candidates);
|
||||
let available: AccountId[] = [];
|
||||
|
||||
if (this.props.electionsInfo) {
|
||||
const { electionsInfo: { candidates, members, runnersUp } } = this.props;
|
||||
|
||||
available = members
|
||||
.map(([accountId]): AccountId => accountId)
|
||||
.concat(runnersUp.map(([accountId]): AccountId => accountId))
|
||||
.concat(candidates);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
isDisabled={available.length === 0}
|
||||
isPrimary
|
||||
label={t('Vote')}
|
||||
label={this.props.t('Vote')}
|
||||
icon='check'
|
||||
onClick={this.showModal}
|
||||
/>
|
||||
@ -82,6 +87,10 @@ class Vote extends TxModal<Props, State> {
|
||||
}
|
||||
|
||||
protected renderContent = (): React.ReactNode => {
|
||||
if (!this.props.electionsInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { electionsInfo: { candidates, members, runnersUp }, t } = this.props;
|
||||
const { accountId, votes } = this.state;
|
||||
const available = members
|
||||
@ -121,6 +130,10 @@ class Vote extends TxModal<Props, State> {
|
||||
(api.query.electionsPhragmen || api.query.elections)
|
||||
.votesOf<[AccountId[]] & Codec>(accountId)
|
||||
.then(([existingVotes]): void => {
|
||||
if (!this.props.electionsInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { electionsInfo: { candidates, members, runnersUp } } = this.props;
|
||||
const available = members
|
||||
.map(([accountId]): string => accountId.toString())
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { DerivedBalancesAll } from '@polkadot/api-derive/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { InputBalance } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { BalanceVoting } from '@polkadot/react-query';
|
||||
@ -23,7 +23,7 @@ interface ValueState {
|
||||
value?: BN | string;
|
||||
}
|
||||
|
||||
export default function VoteValue ({ accountId, onChange }: Props): React.ReactElement<Props> | null {
|
||||
function VoteValue ({ accountId, onChange }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const allBalances = useCall<DerivedBalancesAll>(api.derive.balances.all as any, [accountId]);
|
||||
@ -56,7 +56,10 @@ export default function VoteValue ({ accountId, onChange }: Props): React.ReactE
|
||||
isBn(value) && onChange(value);
|
||||
}, [value]);
|
||||
|
||||
const _setValue = (value?: BN): void => setValue({ selectedId, value });
|
||||
const _setValue = useCallback(
|
||||
(value?: BN): void => setValue({ selectedId, value }),
|
||||
[selectedId]
|
||||
);
|
||||
|
||||
return (
|
||||
<InputBalance
|
||||
@ -69,3 +72,5 @@ export default function VoteValue ({ accountId, onChange }: Props): React.ReactE
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(VoteValue);
|
||||
|
||||
@ -13,7 +13,7 @@ interface Props {
|
||||
voters: AccountId[];
|
||||
}
|
||||
|
||||
export default function Voters ({ voters }: Props): React.ReactElement<Props> | null {
|
||||
function Voters ({ voters }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@ -35,3 +35,5 @@ export default function Voters ({ voters }: Props): React.ReactElement<Props> |
|
||||
</details>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Voters);
|
||||
|
||||
@ -6,10 +6,8 @@ import { DerivedElectionsInfo } from '@polkadot/api-derive/types';
|
||||
import { AccountId, BlockNumber } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import { registry } from '@polkadot/react-api';
|
||||
import { Button } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { createType } from '@polkadot/types';
|
||||
|
||||
import Candidates from './Candidates';
|
||||
import Members from './Members';
|
||||
@ -19,38 +17,32 @@ import Vote from './Vote';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
prime: AccountId | null;
|
||||
}
|
||||
|
||||
const NULL_INFO: DerivedElectionsInfo = {
|
||||
candidates: [],
|
||||
candidateCount: createType(registry, 'u32'),
|
||||
desiredSeats: createType(registry, 'u32'),
|
||||
members: [],
|
||||
runnersUp: [],
|
||||
termDuration: createType(registry, 'BlockNumber')
|
||||
};
|
||||
function transformVotes ([voters, casted]: [AccountId[], AccountId[][]]): Record<string, AccountId[]> {
|
||||
return voters.reduce((result: Record<string, AccountId[]>, voter, index): Record<string, AccountId[]> => {
|
||||
casted[index].forEach((candidate): void => {
|
||||
const address = candidate.toString();
|
||||
|
||||
export default function Overview ({ className }: Props): React.ReactElement<Props> {
|
||||
if (!result[address]) {
|
||||
result[address] = [];
|
||||
}
|
||||
|
||||
result[address].push(voter);
|
||||
});
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function Overview ({ className, prime }: Props): React.ReactElement<Props> {
|
||||
const { api } = useApi();
|
||||
const bestNumber = useCall<BlockNumber>(api.derive.chain.bestNumber, []);
|
||||
const _electionsInfo = useCall<DerivedElectionsInfo>(api.derive.elections.info, []);
|
||||
const electionsInfo = useCall<DerivedElectionsInfo>(api.derive.elections.info, []);
|
||||
const allVotes = useCall<Record<string, AccountId[]>>(api.query.electionsPhragmen?.votesOf, [], {
|
||||
transform: ([voters, casted]: [AccountId[], AccountId[][]]): Record<string, AccountId[]> =>
|
||||
voters.reduce((result: Record<string, AccountId[]>, voter, index): Record<string, AccountId[]> => {
|
||||
casted[index].forEach((candidate): void => {
|
||||
const address = candidate.toString();
|
||||
|
||||
if (!result[address]) {
|
||||
result[address] = [];
|
||||
}
|
||||
|
||||
result[address].push(voter);
|
||||
});
|
||||
|
||||
return result;
|
||||
}, {})
|
||||
transform: transformVotes
|
||||
});
|
||||
const electionsInfo = _electionsInfo || NULL_INFO;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
@ -66,6 +58,7 @@ export default function Overview ({ className }: Props): React.ReactElement<Prop
|
||||
<Members
|
||||
allVotes={allVotes}
|
||||
electionsInfo={electionsInfo}
|
||||
prime={prime}
|
||||
/>
|
||||
<Candidates
|
||||
allVotes={allVotes}
|
||||
@ -74,3 +67,5 @@ export default function Overview ({ className }: Props): React.ReactElement<Prop
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Overview);
|
||||
|
||||
@ -8,7 +8,7 @@ import { DerivedElectionsInfo } from '@polkadot/api-derive/types';
|
||||
import BN from 'bn.js';
|
||||
|
||||
export interface ComponentProps {
|
||||
electionsInfo: DerivedElectionsInfo;
|
||||
electionsInfo?: DerivedElectionsInfo;
|
||||
}
|
||||
|
||||
export interface VoterPosition {
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Option } from '@polkadot/types';
|
||||
import { AccountId } from '@polkadot/types/interfaces';
|
||||
import { DerivedCollectiveProposals } from '@polkadot/api-derive/types';
|
||||
import { AppProps, BareProps } from '@polkadot/react-components/types';
|
||||
|
||||
@ -26,7 +28,11 @@ function CouncilApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
const { api } = useApi();
|
||||
const { pathname } = useLocation();
|
||||
const numMotions = useCounter();
|
||||
const motions = useCall<DerivedCollectiveProposals>(api.derive.council.proposals, []);
|
||||
const prime = useCall<AccountId | null>(api.query.council.prime, [], {
|
||||
transform: (result: Option<AccountId>): AccountId | null => result.unwrapOr(null)
|
||||
}) || null;
|
||||
const motions = useCall<DerivedCollectiveProposals>(api.derive.council.proposals);
|
||||
|
||||
const items = useMemo(() => [
|
||||
{
|
||||
isRoot: true,
|
||||
@ -49,10 +55,16 @@ function CouncilApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
</header>
|
||||
<Switch>
|
||||
<Route path={`${basePath}/motions`}>
|
||||
<Motions motions={motions} />
|
||||
<Motions
|
||||
motions={motions}
|
||||
prime={prime}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
<Overview className={[basePath, `${basePath}/candidates`].includes(pathname) ? '' : 'council--hidden'} />
|
||||
<Overview
|
||||
className={[basePath, `${basePath}/candidates`].includes(pathname) ? '' : 'council--hidden'}
|
||||
prime={prime}
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { Bid } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
@ -15,7 +15,7 @@ interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function Bids ({ className }: Props): React.ReactElement<Props> {
|
||||
function Bids ({ className }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const bids = useCall<Bid[]>(api.query.society.bids, []);
|
||||
@ -23,21 +23,25 @@ export default function Bids ({ className }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<div className={`overviewSection ${className}`}>
|
||||
<h1>{t('bids')}</h1>
|
||||
{bids?.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{bids.map((bid): React.ReactNode => (
|
||||
<BidRow
|
||||
key={bid.who.toString()}
|
||||
value={bid}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: t('No bids')
|
||||
{bids
|
||||
? bids.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{bids.map((bid): React.ReactNode => (
|
||||
<BidRow
|
||||
key={bid.who.toString()}
|
||||
value={bid}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: t('No bids')
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Bids);
|
||||
|
||||
@ -6,7 +6,7 @@ import { DeriveSocietyCandidate } from '@polkadot/api-derive/types';
|
||||
import { OwnMembers } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
@ -24,23 +24,25 @@ export default function Candidates ({ allMembers, className, isMember, ownMember
|
||||
return (
|
||||
<div className={`overviewSection ${className}`}>
|
||||
<h1>{t('candidates')}</h1>
|
||||
{candidates?.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{candidates.map((candidate): React.ReactNode => (
|
||||
<Candidate
|
||||
allMembers={allMembers}
|
||||
isMember={isMember}
|
||||
key={candidate.accountId.toString()}
|
||||
ownMembers={ownMembers}
|
||||
value={candidate}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: t('No candidates')
|
||||
{candidates
|
||||
? candidates.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{candidates.map((candidate): React.ReactNode => (
|
||||
<Candidate
|
||||
allMembers={allMembers}
|
||||
isMember={isMember}
|
||||
key={candidate.accountId.toString()}
|
||||
ownMembers={ownMembers}
|
||||
value={candidate}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: t('No candidates')
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -5,34 +5,39 @@
|
||||
import { DeriveSocietyMember } from '@polkadot/api-derive/types';
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { AddressSmall, Badge, Icon } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
isHead?: boolean;
|
||||
value: DeriveSocietyMember;
|
||||
}
|
||||
|
||||
export default function Member ({ isHead, value: { accountId, strikes } }: Props): React.ReactElement<Props> {
|
||||
function Member ({ className, isHead, value: { accountId, strikes } }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td className={`${isHead && 'head-icon'}`}>
|
||||
{isHead && (
|
||||
<Badge
|
||||
hover={t('Current head')}
|
||||
info={<Icon name='check' />}
|
||||
isInline
|
||||
isTooltip
|
||||
type='green'
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<tr className={`${className} ${isHead && 'society--isHead'}`}>
|
||||
<td className='top'>
|
||||
<AddressSmall value={accountId} />
|
||||
</td>
|
||||
<td className='society--head'>
|
||||
{isHead && (
|
||||
<div>
|
||||
<Badge
|
||||
hover={t('Current head')}
|
||||
info={<Icon name='chess king' />}
|
||||
isInline
|
||||
isTooltip
|
||||
type='green'
|
||||
/>
|
||||
<span> {t('society head')}</span>
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
<td className='number top'>
|
||||
<label>{t('strikes')}</label>
|
||||
{strikes.toString()}
|
||||
@ -40,3 +45,21 @@ export default function Member ({ isHead, value: { accountId, strikes } }: Props
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(styled(Member)`
|
||||
&.society--isHead {
|
||||
td {
|
||||
background-color: rgba(239, 255, 239, 0.8) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.society--head > div {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
|
||||
> span {
|
||||
color: green;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { DeriveSociety, DeriveSocietyMember } from '@polkadot/api-derive/types';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { Spinner, Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
@ -31,21 +31,23 @@ export default function Members ({ className, info }: Props): React.ReactElement
|
||||
return (
|
||||
<div className={`overviewSection ${className}`}>
|
||||
<h1>{t('members')}</h1>
|
||||
{filtered.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{filtered.map((member): React.ReactNode => (
|
||||
<Member
|
||||
isHead={info?.head?.eq(member.accountId)}
|
||||
key={member.accountId.toString()}
|
||||
value={member}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: t('No active members')
|
||||
{info
|
||||
? filtered.length
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{filtered.map((member): React.ReactNode => (
|
||||
<Member
|
||||
isHead={info?.head?.eq(member.accountId)}
|
||||
key={member.accountId.toString()}
|
||||
value={member}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
: t('No active members')
|
||||
: <Spinner />
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { DeriveSociety } from '@polkadot/api-derive/types';
|
||||
import { BlockNumber } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { SummaryBox, CardSummary } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
@ -24,9 +24,11 @@ function Summary ({ className, info }: Props): React.ReactElement<Props> {
|
||||
const members = useCall<any[]>(api.derive.society.members, []);
|
||||
const bestNumber = useCall<BlockNumber>(api.derive.chain.bestNumber, []);
|
||||
|
||||
const pot = info?.pot.gtn(0)
|
||||
? info.pot.toString()
|
||||
: null;
|
||||
const pot = useMemo((): string | null => {
|
||||
return info?.pot.gtn(0)
|
||||
? info.pot.toString()
|
||||
: null;
|
||||
}, [info]);
|
||||
|
||||
return (
|
||||
<SummaryBox className={className}>
|
||||
@ -62,12 +64,14 @@ function Summary ({ className, info }: Props): React.ReactElement<Props> {
|
||||
</>
|
||||
)}
|
||||
<section>
|
||||
<CardSummary label={t('pot')}>
|
||||
<FormatBalance
|
||||
value={pot}
|
||||
withSi
|
||||
/>
|
||||
</CardSummary>
|
||||
{pot && (
|
||||
<CardSummary label={t('pot')}>
|
||||
<FormatBalance
|
||||
value={pot}
|
||||
withSi
|
||||
/>
|
||||
</CardSummary>
|
||||
)}
|
||||
</section>
|
||||
</SummaryBox>
|
||||
);
|
||||
|
||||
@ -47,11 +47,5 @@ function Overview ({ className }: Props): React.ReactElement<Props> {
|
||||
export default React.memo(styled(Overview)`
|
||||
.overviewSection {
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
.head-icon {
|
||||
padding-left: 1.5rem;
|
||||
text-align: center;
|
||||
width: 1rem;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
@ -5,16 +5,18 @@
|
||||
import { AccountId } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import { AddressSmall, Table } from '@polkadot/react-components';
|
||||
import styled from 'styled-components';
|
||||
import { AddressSmall, Badge, Icon, Table } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
members?: AccountId[];
|
||||
prime?: AccountId | null;
|
||||
}
|
||||
|
||||
export default function Members ({ className, members }: Props): React.ReactElement<Props> {
|
||||
function Members ({ className, members, prime }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@ -23,13 +25,32 @@ export default function Members ({ className, members }: Props): React.ReactElem
|
||||
? (
|
||||
<Table>
|
||||
<Table.Body>
|
||||
{members.map((accountId): React.ReactNode => (
|
||||
<tr key={accountId.toString()}>
|
||||
<td>
|
||||
<AddressSmall value={accountId} />
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{members.map((accountId): React.ReactNode => {
|
||||
const isPrime = prime?.toString() === accountId.toString();
|
||||
|
||||
return (
|
||||
<tr
|
||||
className={isPrime ? 'techcomm--isPrime' : ''}
|
||||
key={accountId.toString()}
|
||||
>
|
||||
<td className='all'>
|
||||
<AddressSmall value={accountId} />
|
||||
</td>
|
||||
<td className='right techcomm--prime'>
|
||||
{isPrime && (
|
||||
<div>
|
||||
<Badge
|
||||
info={<Icon name='chess king' />}
|
||||
isInline
|
||||
type='green'
|
||||
/>
|
||||
<span>{' '}{t('prime voter')}</span>
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
@ -38,3 +59,18 @@ export default function Members ({ className, members }: Props): React.ReactElem
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default styled(Members)`
|
||||
.techcomm--isPrime td {
|
||||
background: rgba(239, 255, 239, 0.8);
|
||||
}
|
||||
.techcomm--prime > div {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
|
||||
> span {
|
||||
color: green;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -9,14 +9,17 @@ import React from 'react';
|
||||
import Members from './Members';
|
||||
import Summary from './Summary';
|
||||
|
||||
export default function Overview ({ className, members, proposals }: Props): React.ReactElement<Props> {
|
||||
export default function Overview ({ className, members, prime, proposals }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<div className={className}>
|
||||
<Summary
|
||||
members={members}
|
||||
proposals={proposals}
|
||||
/>
|
||||
<Members members={members} />
|
||||
<Members
|
||||
members={members}
|
||||
prime={prime}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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 { Proposal as ProposalType, Votes } from '@polkadot/types/interfaces';
|
||||
import { AccountId, Proposal as ProposalType, Votes } from '@polkadot/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
import { AddressMini } from '@polkadot/react-components';
|
||||
@ -17,9 +17,10 @@ import Voting from './Voting';
|
||||
interface Props {
|
||||
className?: string;
|
||||
hash: string;
|
||||
prime?: AccountId | null;
|
||||
}
|
||||
|
||||
export default function Proposal ({ className, hash }: Props): React.ReactElement<Props> | null {
|
||||
export default function Proposal ({ className, hash, prime }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const optProposal = useCall<Option<ProposalType>>(api.query.technicalCommittee.proposalOf, [hash]);
|
||||
@ -67,6 +68,7 @@ export default function Proposal ({ className, hash }: Props): React.ReactElemen
|
||||
<td className='number top together'>
|
||||
<Voting
|
||||
hash={hash}
|
||||
prime={prime}
|
||||
proposalId={index}
|
||||
/>
|
||||
</td>
|
||||
|
||||
@ -2,9 +2,11 @@
|
||||
// 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 BN from 'bn.js';
|
||||
import React, { useState } from 'react';
|
||||
import { Button, Modal, VoteAccount, VoteActions, VoteToggle } from '@polkadot/react-components';
|
||||
import { Button, Icon, Modal, VoteAccount, VoteActions, VoteToggle } from '@polkadot/react-components';
|
||||
import { useAccounts, useToggle } from '@polkadot/react-hooks';
|
||||
import { isBoolean } from '@polkadot/util';
|
||||
|
||||
@ -12,10 +14,11 @@ import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
hash: string;
|
||||
prime?: AccountId | null;
|
||||
proposalId: BN | number;
|
||||
}
|
||||
|
||||
export default function Voting ({ hash, proposalId }: Props): React.ReactElement<Props> | null {
|
||||
export default function Voting ({ hash, prime, proposalId }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { hasAccounts } = useAccounts();
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
@ -27,6 +30,7 @@ export default function Voting ({ hash, proposalId }: Props): React.ReactElement
|
||||
}
|
||||
|
||||
const _onChangeVote = (vote?: boolean): void => setVoteValue(isBoolean(vote) ? vote : true);
|
||||
const isPrime = accountId === prime?.toString();
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -41,6 +45,11 @@ export default function Voting ({ hash, proposalId }: Props): React.ReactElement
|
||||
onChange={_onChangeVote}
|
||||
value={voteValue}
|
||||
/>
|
||||
{isPrime && (
|
||||
<article className='warning'>
|
||||
<div><Icon name='warning sign' />{t('You are voting with this collective\'s prime account. The vote will be the default outcome in case of any abstentions.')}</div>
|
||||
</article>
|
||||
)}
|
||||
</Modal.Content>
|
||||
<VoteActions
|
||||
accountId={accountId}
|
||||
|
||||
@ -13,7 +13,7 @@ import { useTranslation } from '../translate';
|
||||
import Proposal from './Proposal';
|
||||
import Propose from './Propose';
|
||||
|
||||
export default function Proposals ({ className, members, proposals }: Props): React.ReactElement<Props> {
|
||||
export default function Proposals ({ className, members, prime, proposals }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [isProposeOpen, togglePropose] = useToggle(false);
|
||||
|
||||
@ -41,6 +41,7 @@ export default function Proposals ({ className, members, proposals }: Props): Re
|
||||
<Proposal
|
||||
hash={hash.toHex()}
|
||||
key={hash.toHex()}
|
||||
prime={prime}
|
||||
/>
|
||||
))}
|
||||
</Table.Body>
|
||||
|
||||
@ -2,6 +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 { Option } from '@polkadot/types';
|
||||
import { AccountId, Hash } from '@polkadot/types/interfaces';
|
||||
import { AppProps, BareProps } from '@polkadot/react-components/types';
|
||||
|
||||
@ -21,8 +22,11 @@ interface Props extends AppProps, BareProps {}
|
||||
export default function TechCommApp ({ basePath, className }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const members = useCall<AccountId[]>(api.query.technicalCommittee.members, []);
|
||||
const proposals = useCall<Hash[]>(api.query.technicalCommittee.proposals, []);
|
||||
const members = useCall<AccountId[]>(api.query.technicalCommittee.members);
|
||||
const prime = useCall<AccountId | null>(api.query.council.prime, [], {
|
||||
transform: (result: Option<AccountId>): AccountId | null => result?.unwrapOr(null) || null
|
||||
}) || null;
|
||||
const proposals = useCall<Hash[]>(api.query.technicalCommittee.proposals);
|
||||
const items = useMemo(() => [
|
||||
{
|
||||
isRoot: true,
|
||||
@ -47,12 +51,14 @@ export default function TechCommApp ({ basePath, className }: Props): React.Reac
|
||||
<Route path={`${basePath}/proposals`}>
|
||||
<Proposals
|
||||
members={members}
|
||||
prime={prime}
|
||||
proposals={proposals}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={basePath}>
|
||||
<Overview
|
||||
members={members}
|
||||
prime={prime}
|
||||
proposals={proposals}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
@ -6,6 +6,7 @@ import { AccountId, Hash } from '@polkadot/types/interfaces';
|
||||
|
||||
export interface ComponentProps {
|
||||
className?: string;
|
||||
prime?: AccountId | null;
|
||||
proposals?: Hash[];
|
||||
members?: AccountId[];
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user