// Copyright 2017-2026 @pezkuwi/app-democracy authors & contributors // SPDX-License-Identifier: Apache-2.0 import type { DeriveReferendumExt } from '@pezkuwi/api-derive/types'; import type { Balance } from '@pezkuwi/types/interfaces'; import React, { useMemo } from 'react'; import { Badge, Button, Columar, ExpandButton, Icon, LinkExternal, Progress, Table } from '@pezkuwi/react-components'; import { useAccounts, useApi, useBestNumber, useCall, useToggle } from '@pezkuwi/react-hooks'; import { BlockToTime } from '@pezkuwi/react-query'; import { BN, BN_ONE, formatNumber, isBoolean } from '@pezkuwi/util'; import { useTranslation } from '../translate.js'; import useChangeCalc from '../useChangeCalc.js'; import PreImageButton from './PreImageButton.js'; import ProposalCell from './ProposalCell.js'; import ReferendumVotes from './ReferendumVotes.js'; import Voting from './Voting.js'; interface Props { className?: string; value: DeriveReferendumExt; } interface Percentages { aye: string; nay: string; turnout: string; } interface VoteType { hasVoted: boolean; hasVotedAye: boolean; } function percentage (val: BN, div: BN): string { return Math.min(100, val.muln(10000).div(div).toNumber() / 100).toFixed(2); } function Referendum ({ className = '', value: { allAye, allNay, image, imageHash, index, isPassing, status, voteCountAye, voteCountNay, votedAye, votedNay, votedTotal } }: Props): React.ReactElement | null { const { t } = useTranslation(); const { api } = useApi(); const { allAccounts } = useAccounts(); const bestNumber = useBestNumber(); const [isExpanded, toggleIsExpanded] = useToggle(false); const totalIssuance = useCall(api.query.balances?.totalIssuance); const { changeAye, changeNay } = useChangeCalc(status.threshold, votedAye, votedNay, votedTotal); const threshold = useMemo( () => status.threshold.type.toString().replace('majority', ' majority '), [status] ); const totalCalculated = votedAye.add(votedNay); const [percentages, { hasVoted, hasVotedAye }] = useMemo( (): [Percentages | null, VoteType] => { if (totalIssuance) { const aye = allAye.reduce((total: BN, { balance }) => total.add(balance), new BN(0)); const nay = allNay.reduce((total: BN, { balance }) => total.add(balance), new BN(0)); const hasVotedAye = allAye.some(({ accountId }) => allAccounts.includes(accountId.toString())); return [ { aye: votedTotal.isZero() ? '' : `${percentage(aye, votedTotal)}%`, nay: votedTotal.isZero() ? '' : `${percentage(nay, votedTotal)}%`, turnout: `${percentage(votedTotal, totalIssuance)}%` }, { hasVoted: hasVotedAye || allNay.some(({ accountId }) => allAccounts.includes(accountId.toString())), hasVotedAye } ]; } else { return [null, { hasVoted: false, hasVotedAye: false }]; } }, [allAccounts, allAye, allNay, totalIssuance, votedTotal] ); if (!bestNumber || status.end.sub(bestNumber).lten(0)) { return null; } const enactBlock = status.end.add(status.delay); const remainBlock = status.end.sub(bestNumber).isub(BN_ONE); return ( <> {t('{{blocks}} blocks', { replace: { blocks: formatNumber(remainBlock) } })} #{formatNumber(enactBlock)} {percentages && ( <>
{percentages.turnout}
)} {isBoolean(isPassing) && ( )} {!image?.proposal && ( )} ); } export default React.memo(Referendum);