diff --git a/packages/page-council/src/Motions/Close.tsx b/packages/page-council/src/Motions/Close.tsx index 15597b3cab..c911ef1474 100644 --- a/packages/page-council/src/Motions/Close.tsx +++ b/packages/page-council/src/Motions/Close.tsx @@ -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 | null { const { t } = useTranslation(); + const { api } = useApi(); + const { allAccounts } = useAccounts(); const [isOpen, toggleOpen] = useToggle(); const [accountId, setAccountId] = useState(null); + const [proposalLength] = useState(proposal.encodedLength); + const [proposalWeight, setProposalWeight] = useState(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 diff --git a/packages/page-council/src/Motions/Motion.tsx b/packages/page-council/src/Motions/Motion.tsx index 2db093011f..8f448b7c49 100644 --- a/packages/page-council/src/Motions/Motion.tsx +++ b/packages/page-council/src/Motions/Motion.tsx @@ -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 | null { const { api } = useApi(); const bestNumber = useCall(api.derive.chain.bestNumber, []); + const [{ isCloseable, isVotable }, setStatus] = useState({ 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, )} - - + + - {bestNumber && ( - // end may not be existing (older versions) - !end || end.gt(bestNumber) - ? ( - - ) - : ( - - ) + {isVotable && ( + + )} + {isCloseable && ( + )} diff --git a/packages/page-council/src/Motions/ProposeExternal.tsx b/packages/page-council/src/Motions/ProposeExternal.tsx index 1e8f1a1885..3f5cce94cf 100644 --- a/packages/page-council/src/Motions/ProposeExternal.tsx +++ b/packages/page-council/src/Motions/ProposeExternal.tsx @@ -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 { const { t } = useTranslation(); const { api } = useApi(); const [isVisible, toggleVisible] = useToggle(); const [accountId, setAcountId] = useState(null); - const [proposal, setProposal] = useState | null>(null); + const [{ proposal, proposalLength }, setProposal] = useState({ proposalLength: 0 }); const [{ hash, isHashValid }, setHash] = useState({ 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('Propose')} onStart={toggleVisible} - params={[threshold, proposal]} + params={ + api.tx.council.propose.meta.args.length === 3 + ? [threshold, proposal, proposalLength] + : [threshold, proposal] + } tx='council.propose' /> diff --git a/packages/page-council/src/Motions/ProposeMotion.tsx b/packages/page-council/src/Motions/ProposeMotion.tsx index 8b442342d5..ea2ba33ce3 100644 --- a/packages/page-council/src/Motions/ProposeMotion.tsx +++ b/packages/page-council/src/Motions/ProposeMotion.tsx @@ -21,12 +21,17 @@ interface Threshold { threshold?: BN; } +interface ProposalState { + proposal?: SubmittableExtrinsic<'promise'> | null; + proposalLength: number; +} + function Propose ({ isMember, members }: Props): React.ReactElement { const { t } = useTranslation(); - const { apiDefaultTxSudo } = useApi(); + const { api, apiDefaultTxSudo } = useApi(); const [isOpen, toggleOpen] = useToggle(); const [accountId, setAcountId] = useState(null); - const [method, setMethod] = useState | null | undefined>(); + const [{ proposal, proposalLength }, setProposal] = useState({ proposalLength: 0 }); const [{ isThresholdValid, threshold }, setThreshold] = useState({ isThresholdValid: false }); useEffect((): void => { @@ -37,7 +42,10 @@ function Propose ({ isMember, members }: Props): React.ReactElement { }, [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 { ('Propose')} - params={[threshold, method]} + onStart={toggleOpen} + params={ + api.tx.council.propose.meta.args.length === 3 + ? [threshold, proposal, proposalLength] + : [threshold, proposal] + } tx='council.propose' />