Close on proposal failing (#2808)

This commit is contained in:
Jaco Greeff 2020-05-25 15:21:47 +02:00 committed by GitHub
parent 332b076c8d
commit c385a8afad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 47 deletions

View File

@ -2,20 +2,19 @@
// 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, Votes } from '@polkadot/types/interfaces';
import { AccountId } from '@polkadot/types/interfaces';
import { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import React, { useEffect, useState } from 'react';
import { ApiPromise } from '@polkadot/api';
import React from 'react';
import ProposalCell from '@polkadot/app-democracy/Overview/ProposalCell';
import { LinkExternal } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { BlockToTime } from '@polkadot/react-query';
import { formatNumber } from '@polkadot/util';
import Close from './Close';
import Voters from './Votes';
import Voting from './Voting';
import useVotingStatus from './useVotingStatus';
interface Props {
className?: string;
@ -25,44 +24,8 @@ 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]);
const { isCloseable, isVoteable, remainingBlocks } = useVotingStatus(votes, members.length);
if (!votes) {
return null;
@ -81,9 +44,9 @@ function Motion ({ className = '', isMember, members, motion: { hash, proposal,
{formatNumber(ayes.length)}/{formatNumber(threshold)}
</td>
<td className='number together'>
{bestNumber && end && (
{remainingBlocks && end && (
<>
<BlockToTime blocks={end.sub(bestNumber)} />
<BlockToTime blocks={remainingBlocks} />
#{formatNumber(end)}
</>
)}
@ -91,7 +54,7 @@ function Motion ({ className = '', isMember, members, motion: { hash, proposal,
<Voters votes={ayes} />
<Voters votes={nays} />
<td className='button'>
{isVotable && (
{isVoteable && (
<Voting
hash={hash}
idNumber={index}

View File

@ -0,0 +1,54 @@
// Copyright 2017-2020 @polkadot/app-democracy authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { BlockNumber, Votes } from '@polkadot/types/interfaces';
import BN from 'bn.js';
import { useEffect, useState } from 'react';
import { ApiPromise } from '@polkadot/api';
import { useApi, useCall } from '@polkadot/react-hooks';
interface State {
isCloseable: boolean;
isVoteable: boolean;
remainingBlocks: BN | null;
}
function getStatus (api: ApiPromise, bestNumber: BlockNumber, votes: Votes, numMembers: number): State {
if (!votes.end) {
return {
isCloseable: false,
isVoteable: true,
remainingBlocks: null
};
}
const isEnd = bestNumber.gte(votes.end);
const isPassing = votes.threshold.lten(votes.ayes.length);
const isFailing = votes.threshold.gtn(Math.abs(numMembers - votes.nays.length));
return {
isCloseable: api.tx.council.close.meta.args.length === 2
? isEnd
: isEnd || isPassing || isFailing,
isVoteable: !isEnd,
remainingBlocks: isEnd
? null
: votes.end.sub(bestNumber)
};
}
export default function useVotingStatus (votes: Votes | null, numMembers: number): State {
const { api } = useApi();
const bestNumber = useCall<BlockNumber>(api.derive.chain.bestNumber, []);
const [state, setState] = useState<State>({ isCloseable: false, isVoteable: false, remainingBlocks: null });
useEffect((): void => {
bestNumber && votes && setState(
getStatus(api, bestNumber, votes, numMembers)
);
}, [api, bestNumber, numMembers, votes]);
return state;
}

View File

@ -22,14 +22,16 @@ interface Props {
function Proposal ({ className = '', imageHash, prime }: Props): React.ReactElement<Props> | null {
const { api } = useApi();
const optProposal = useCall<Option<ProposalType>>(api.query.technicalCommittee.proposalOf, [imageHash]);
const votes = useCall<Option<Votes>>(api.query.technicalCommittee.voting, [imageHash]);
const votes = useCall<Votes | null>(api.query.technicalCommittee.voting, [imageHash], {
transform: (optVotes: Option<Votes>) => optVotes.unwrapOr(null)
});
if (!optProposal?.isSome || !votes?.isSome) {
if (!optProposal?.isSome || !votes) {
return null;
}
const proposal = optProposal.unwrap();
const { ayes, index, nays, threshold } = votes.unwrap();
const { ayes, index, nays, threshold } = votes;
return (
<tr className={className}>