// Copyright 2017-2026 @pezkuwi/app-democracy authors & contributors // SPDX-License-Identifier: Apache-2.0 import type { SubmittableExtrinsic } from '@pezkuwi/api/types'; import type { Hash, VoteThreshold } from '@pezkuwi/types/interfaces'; import type { HexString } from '@pezkuwi/util/types'; import React, { useEffect, useMemo, useState } from 'react'; import { getFastTrackThreshold } from '@pezkuwi/apps-config'; import { Button, Input, InputAddress, InputNumber, Modal, Toggle, TxButton } from '@pezkuwi/react-components'; import { useApi, useCall, useCollectiveInstance, useToggle } from '@pezkuwi/react-hooks'; import { BN, isString } from '@pezkuwi/util'; import { useTranslation } from '../translate.js'; interface Props { imageHash: Hash | HexString; members: string[]; threshold: VoteThreshold; } interface ProposalState { proposal?: SubmittableExtrinsic<'promise'> | null; proposalLength: number; } // default, assuming 6s blocks const ONE_HOUR = (60 * 60) / 6; const DEF_DELAY = new BN(ONE_HOUR); const DEF_VOTING = new BN(3 * ONE_HOUR); function Fasttrack ({ imageHash, members, threshold }: Props): React.ReactElement | null { const { t } = useTranslation(); const { api } = useApi(); const [isFasttrackOpen, toggleFasttrack] = useToggle(); const [accountId, setAcountId] = useState(null); const [delayBlocks, setDelayBlocks] = useState(DEF_DELAY); const [votingBlocks, setVotingBlocks] = useState(api.consts.democracy.fastTrackVotingPeriod || DEF_VOTING); const [{ proposal, proposalLength }, setProposal] = useState(() => ({ proposalLength: 0 })); const [withVote, toggleVote] = useToggle(true); const modLocation = useCollectiveInstance('technicalCommittee'); const proposalCount = useCall(modLocation && api.query[modLocation].proposalCount); const memberThreshold = useMemo( () => new BN( Math.ceil( members.length * getFastTrackThreshold(api, !votingBlocks || api.consts.democracy.fastTrackVotingPeriod.lte(votingBlocks)) ) ), [api, members, votingBlocks] ); const extrinsic = useMemo( (): SubmittableExtrinsic<'promise'> | null => { if (!modLocation || !proposal || !proposalCount || !api.tx.utility) { return null; } const proposeTx = api.tx[modLocation].propose.meta.args.length === 3 ? api.tx[modLocation].propose(memberThreshold, proposal, proposalLength) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore Old-type : api.tx[modLocation].propose(memberThreshold, proposal); return withVote && (members.length > 1) ? api.tx.utility.batch([ proposeTx, api.tx[modLocation].vote(proposal.method.hash, proposalCount, true) ]) : proposeTx; }, [api, members, memberThreshold, modLocation, proposal, proposalCount, proposalLength, withVote] ); useEffect((): void => { const proposal = delayBlocks && !delayBlocks.isZero() && votingBlocks && !votingBlocks.isZero() ? api.tx.democracy.fastTrack(imageHash, votingBlocks, delayBlocks) : null; setProposal({ proposal, proposalLength: proposal?.length || 0 }); }, [api, delayBlocks, imageHash, members, votingBlocks]); if (!modLocation || !api.tx.utility) { return null; } return ( <> {isFasttrackOpen && ( {(members.length > 1) && ( )} )}