Council propose/close with length & weights (#2807)

* Council propose/close with length & weights

* Adjust for consistency

* Retrieve weight via paymentInfo

* Closing and voting in conjunction
This commit is contained in:
Jaco Greeff 2020-05-25 13:51:52 +02:00 committed by GitHub
parent 82f8fd7576
commit fbbc1dd7fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 116 additions and 40 deletions

View File

@ -2,11 +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 { Hash, Proposal, ProposalIndex } from '@polkadot/types/interfaces';
import { Hash, Proposal, ProposalIndex, Weight } from '@polkadot/types/interfaces';
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { Button, InputAddress, Modal, ProposedAction, TxButton } from '@polkadot/react-components';
import { useToggle } from '@polkadot/react-hooks';
import { useAccounts, useApi, useToggle } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
@ -20,8 +20,18 @@ interface Props {
function Close ({ hash, idNumber, isDisabled, members, proposal }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { api } = useApi();
const { allAccounts } = useAccounts();
const [isOpen, toggleOpen] = useToggle();
const [accountId, setAccountId] = useState<string | null>(null);
const [proposalLength] = useState(proposal.encodedLength);
const [proposalWeight, setProposalWeight] = useState<Weight | null>(null);
useEffect((): void => {
allAccounts.length && (async () => setProposalWeight(
(await api.tx(proposal).paymentInfo(allAccounts[0])).weight
))().catch(console.error);
}, [api, allAccounts, proposal]);
return (
<>
@ -60,8 +70,13 @@ function Close ({ hash, idNumber, isDisabled, members, proposal }: Props): React
<Modal.Actions onCancel={toggleOpen}>
<TxButton
accountId={accountId}
isDisabled={!proposalWeight}
onStart={toggleOpen}
params={[hash, idNumber]}
params={
api.tx.council.close.meta.args.length === 4
? [hash, idNumber, proposalWeight, proposalLength]
: [hash, idNumber]
}
tx='council.close'
/>
</Modal.Actions>

View File

@ -2,10 +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, BlockNumber } from '@polkadot/types/interfaces';
import { AccountId, BlockNumber, Votes } from '@polkadot/types/interfaces';
import { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import React from 'react';
import React, { useEffect, useState } from 'react';
import { ApiPromise } from '@polkadot/api';
import ProposalCell from '@polkadot/app-democracy/Overview/ProposalCell';
import { LinkExternal } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
@ -13,7 +14,7 @@ import { BlockToTime } from '@polkadot/react-query';
import { formatNumber } from '@polkadot/util';
import Close from './Close';
import Votes from './Votes';
import Voters from './Votes';
import Voting from './Voting';
interface Props {
@ -24,9 +25,44 @@ interface Props {
prime: AccountId | null;
}
interface Status {
isCloseable: boolean;
isVotable: boolean;
}
function getStatus (api: ApiPromise, bestNumber: BlockNumber, votes: Votes): Status {
if (!votes.end) {
return {
isCloseable: false,
isVotable: true
};
}
const isEnd = bestNumber.gte(votes.end);
if (api.tx.council.close.meta.args.length === 2) {
return {
isCloseable: isEnd,
isVotable: !isEnd
};
}
return {
isCloseable: isEnd || votes.threshold.eq(votes.ayes.length),
isVotable: !isEnd
};
}
function Motion ({ className = '', isMember, members, motion: { hash, proposal, votes }, prime }: Props): React.ReactElement<Props> | null {
const { api } = useApi();
const bestNumber = useCall<BlockNumber>(api.derive.chain.bestNumber, []);
const [{ isCloseable, isVotable }, setStatus] = useState<Status>({ isCloseable: false, isVotable: false });
useEffect((): void => {
bestNumber && votes && setStatus(
getStatus(api, bestNumber, votes)
);
}, [api, bestNumber, votes]);
if (!votes) {
return null;
@ -52,31 +88,27 @@ function Motion ({ className = '', isMember, members, motion: { hash, proposal,
</>
)}
</td>
<Votes votes={ayes} />
<Votes votes={nays} />
<Voters votes={ayes} />
<Voters votes={nays} />
<td className='button'>
{bestNumber && (
// end may not be existing (older versions)
!end || end.gt(bestNumber)
? (
<Voting
hash={hash}
idNumber={index}
isDisabled={!isMember}
members={members}
prime={prime}
proposal={proposal}
/>
)
: (
<Close
hash={hash}
idNumber={index}
isDisabled={!isMember}
members={members}
proposal={proposal}
/>
)
{isVotable && (
<Voting
hash={hash}
idNumber={index}
isDisabled={!isMember}
members={members}
prime={prime}
proposal={proposal}
/>
)}
{isCloseable && (
<Close
hash={hash}
idNumber={index}
isDisabled={!isMember}
members={members}
proposal={proposal}
/>
)}
</td>
<td className='mini'>

View File

@ -21,13 +21,17 @@ interface HashState {
hash?: string;
isHashValid: boolean;
}
interface ProposalState {
proposal?: SubmittableExtrinsic<'promise'> | null;
proposalLength: number;
}
function ProposeExternal ({ className = '', isMember, members }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [isVisible, toggleVisible] = useToggle();
const [accountId, setAcountId] = useState<string | null>(null);
const [proposal, setProposal] = useState<SubmittableExtrinsic<'promise'> | null>(null);
const [{ proposal, proposalLength }, setProposal] = useState<ProposalState>({ proposalLength: 0 });
const [{ hash, isHashValid }, setHash] = useState<HashState>({ hash: '', isHashValid: false });
const threshold = Math.ceil((members.length || 0) * 0.5);
@ -38,9 +42,17 @@ function ProposeExternal ({ className = '', isMember, members }: Props): React.R
useEffect((): void => {
if (isHashValid && hash) {
setProposal(() => api.tx.democracy.externalProposeMajority(hash));
const proposal = api.tx.democracy.externalProposeMajority(hash);
setProposal({
proposal,
proposalLength: proposal.encodedLength || 0
});
} else {
setProposal(null);
setProposal({
proposal: null,
proposalLength: 0
});
}
}, [api, hash, isHashValid]);
@ -97,7 +109,11 @@ function ProposeExternal ({ className = '', isMember, members }: Props): React.R
isPrimary
label={t<string>('Propose')}
onStart={toggleVisible}
params={[threshold, proposal]}
params={
api.tx.council.propose.meta.args.length === 3
? [threshold, proposal, proposalLength]
: [threshold, proposal]
}
tx='council.propose'
/>
</Modal.Actions>

View File

@ -21,12 +21,17 @@ interface Threshold {
threshold?: BN;
}
interface ProposalState {
proposal?: SubmittableExtrinsic<'promise'> | null;
proposalLength: number;
}
function Propose ({ isMember, members }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { apiDefaultTxSudo } = useApi();
const { api, apiDefaultTxSudo } = useApi();
const [isOpen, toggleOpen] = useToggle();
const [accountId, setAcountId] = useState<string | null>(null);
const [method, setMethod] = useState<SubmittableExtrinsic<'promise'> | null | undefined>();
const [{ proposal, proposalLength }, setProposal] = useState<ProposalState>({ proposalLength: 0 });
const [{ isThresholdValid, threshold }, setThreshold] = useState<Threshold>({ isThresholdValid: false });
useEffect((): void => {
@ -37,7 +42,10 @@ function Propose ({ isMember, members }: Props): React.ReactElement<Props> {
}, [members]);
const _setMethod = useCallback(
(method?: SubmittableExtrinsic<'promise'> | null) => setMethod(() => method),
(proposal?: SubmittableExtrinsic<'promise'> | null) => setProposal({
proposal,
proposalLength: proposal?.encodedLength || 0
}),
[]
);
@ -110,9 +118,14 @@ function Propose ({ isMember, members }: Props): React.ReactElement<Props> {
<Modal.Actions onCancel={toggleOpen}>
<TxButton
accountId={accountId}
isDisabled={!method || !isThresholdValid}
isDisabled={!proposal || !isThresholdValid}
label={t<string>('Propose')}
params={[threshold, method]}
onStart={toggleOpen}
params={
api.tx.council.propose.meta.args.length === 3
? [threshold, proposal, proposalLength]
: [threshold, proposal]
}
tx='council.propose'
/>
</Modal.Actions>