diff --git a/plugins/lockToVote/components/proposal/description.tsx b/plugins/lockToVote/components/proposal/description.tsx index f8c28978..c6cdb8da 100644 --- a/plugins/lockToVote/components/proposal/description.tsx +++ b/plugins/lockToVote/components/proposal/description.tsx @@ -1,67 +1,11 @@ -import { Action } from "@/utils/types"; import { Proposal } from "@/plugins/lockToVote/utils/types"; -import { whatsabi } from "@shazow/whatsabi"; -import { ReactNode, useCallback, useEffect, useState } from "react"; -import { usePublicClient } from "wagmi"; -import { Address, decodeFunctionData } from "viem"; -import { Else, If, IfCase, IfNot, Then } from "@/components/if"; -import { PleaseWaitSpinner } from "@/components/please-wait"; -import { AddressText } from "@/components/text/address"; -import { isAddress } from "@/utils/evm"; import * as DOMPurify from "dompurify"; -import { PUB_CHAIN, PUB_ETHERSCAN_API_KEY } from "@/constants"; +import { ActionCard } from "@/components/actions/action"; +import { If } from "@/components/if"; -const DEFAULT_PROPOSAL_SUMMARY = "(No description available)"; - -type FunctionData = { - args: readonly unknown[] | undefined; - functionName: string; - to: Address; -}; +const DEFAULT_PROPOSAL_METADATA_SUMMARY = "(No description available)"; export default function ProposalDescription(proposal: Proposal) { - const publicClient = usePublicClient({ chainId: PUB_CHAIN.id }); - const [decodedActions, setDecodedActions] = useState([]); - const proposalActions = proposal?.actions || []; - - const getFunctionData = async (action: Action) => { - if (!publicClient) return; - - const abiLoader = new whatsabi.loaders.EtherscanABILoader({ - apiKey: PUB_ETHERSCAN_API_KEY, - }); - - const { abi } = await whatsabi.autoload(action.to, { - provider: publicClient, - abiLoader, - followProxies: true, - }); - - return decodeFunctionData({ - abi, - data: action.data as Address, - }); - }; - - const fetchActionData = useCallback(async () => { - const decodedActions = await Promise.all( - proposalActions.map(async (action) => { - let functionData: any; - if (action.data != "0x") { - functionData = await getFunctionData(action); - } else { - functionData = { functionName: "transfer", args: [action.value] }; - } - return { ...functionData, to: action.to } as FunctionData; - }) - ); - setDecodedActions(decodedActions); - }, [proposal]); - - useEffect(() => { - fetchActionData(); - }, [proposal.actions]); - return (

Actions

-
- +
+

The proposal has no actions

- - - - {decodedActions?.map?.((action, i) => ( - + {proposal.actions?.map?.((action, i) => ( +
+ +
))}
); } - -// This should be encapsulated as soon as ODS exports this widget -const Card = function ({ children }: { children: ReactNode }) { - return ( -
- {children} -
- ); -}; - -const ActionCard = function ({ - action, - idx, -}: { - action: FunctionData; - idx: number; -}) { - return ( - -
-
-

Target contract

-

- {action.to} -

-
-
- {idx + 1} -
-
- -
-

Function name

-

- {action.functionName} -

-
- -
-

Parameters

-
    - {action?.args?.length && - action?.args?.map((arg: any, j: number) => ( -
  • - - - {arg.toString()} - - - {arg.toString()} - - -
  • - ))} -
-
-
- ); -}; diff --git a/plugins/lockToVote/components/proposal/header.tsx b/plugins/lockToVote/components/proposal/header.tsx index 39bab433..19802e13 100644 --- a/plugins/lockToVote/components/proposal/header.tsx +++ b/plugins/lockToVote/components/proposal/header.tsx @@ -1,90 +1,35 @@ -import { useEffect } from "react"; import { Button, Tag } from "@aragon/ods"; -import { Proposal } from "@/plugins/lockToVote/utils/types"; +import { Proposal } from "@/plugins/dualGovernance/utils/types"; import { AlertVariant } from "@aragon/ods"; -import { Else, If, IfCase, Then } from "@/components/if"; +import { ElseIf, If, Then, Else } from "@/components/if"; import { AddressText } from "@/components/text/address"; -import { useWaitForTransactionReceipt, useWriteContract } from "wagmi"; -import { OptimisticTokenVotingPluginAbi } from "../../artifacts/OptimisticTokenVotingPlugin.sol"; -import { AlertContextProps, useAlertContext } from "@/context/AlertContext"; -import { useProposalVariantStatus } from "../../hooks/useProposalVariantStatus"; -import { PUB_CHAIN, PUB_DUAL_GOVERNANCE_PLUGIN_ADDRESS } from "@/constants"; +import { useProposalVariantStatus } from "@/plugins/lockToVote/hooks/useProposalVariantStatus"; import { PleaseWaitSpinner } from "@/components/please-wait"; -import { useRouter } from "next/router"; +import dayjs from "dayjs"; const DEFAULT_PROPOSAL_TITLE = "(No proposal title)"; interface ProposalHeaderProps { proposalNumber: number; proposal: Proposal; - userCanVeto: boolean; - transactionLoading: boolean; + canVeto: boolean; + canExecute: boolean; + transactionConfirming: boolean; onVetoPressed: () => void; + onExecutePressed: () => void; } const ProposalHeader: React.FC = ({ proposalNumber, proposal, - userCanVeto, - transactionLoading, + canVeto, + canExecute, + transactionConfirming, onVetoPressed, + onExecutePressed, }) => { - const { reload } = useRouter(); - const { addAlert } = useAlertContext() as AlertContextProps; const proposalVariant = useProposalVariantStatus(proposal); - - const { - writeContract: executeWrite, - data: executeTxHash, - error, - status, - } = useWriteContract(); - const { isLoading: isConfirming, isSuccess: isConfirmed } = - useWaitForTransactionReceipt({ hash: executeTxHash }); - - const executeButtonPressed = () => { - executeWrite({ - chainId: PUB_CHAIN.id, - abi: OptimisticTokenVotingPluginAbi, - address: PUB_DUAL_GOVERNANCE_PLUGIN_ADDRESS, - functionName: "execute", - args: [proposalNumber], - }); - }; - - useEffect(() => { - if (status === "idle" || status === "pending") return; - else if (status === "error") { - if (error?.message?.startsWith("User rejected the request")) { - addAlert("Transaction rejected by the user", { - timeout: 4 * 1000, - }); - } else { - console.error(error); - addAlert("Could not execute the proposal", { type: "error" }); - } - return; - } - - // success - if (!executeTxHash) return; - else if (isConfirming) { - addAlert("Proposal submitted", { - description: "Waiting for the transaction to be validated", - type: "info", - txHash: executeTxHash, - }); - return; - } else if (!isConfirmed) return; - - addAlert("Proposal executed", { - description: "The transaction has been validated", - type: "success", - txHash: executeTxHash, - }); - - setTimeout(() => reload(), 1000 * 2); - }, [status, executeTxHash, isConfirming, isConfirmed]); + const ended = proposal.parameters.endDate <= Date.now() / 1000; return (
@@ -102,44 +47,38 @@ const ProposalHeader: React.FC = ({ />
- Proposal {proposalNumber + 1} + Proposal {proposalNumber}
-
- +
+ - - - - - -
- -
-
-
+
+ +
- - - - - - + + + + + + +
@@ -147,7 +86,23 @@ const ProposalHeader: React.FC = ({ {proposal.title || DEFAULT_PROPOSAL_TITLE}

- Proposed by {proposal?.creator} + Proposed by {proposal?.creator},{" "} + + + ended on{" "} + {dayjs(Number(proposal.parameters.endDate) * 1000).format( + "D MMM YYYY HH:mm" + )} + h + + + ending on{" "} + {dayjs(Number(proposal.parameters.endDate) * 1000).format( + "D MMM YYYY HH:mm" + )} + h + +

);