From 47c3fa2eb2e5d76076ab148485967bc0fa511b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8r=E2=88=82=C2=A1?= <4456749@users.noreply.github.com> Date: Fri, 23 Feb 2024 11:11:31 +0100 Subject: [PATCH] Removing all alert shorthands --- context/AlertContext.tsx | 49 +- .../components/proposal/header.tsx | 14 +- plugins/dualGovernance/pages/new.tsx | 34 +- plugins/dualGovernance/pages/proposal.tsx | 15 +- plugins/tokenVoting/pages/new.tsx | 446 ++++++++++-------- plugins/tokenVoting/pages/proposal.tsx | 15 +- 6 files changed, 295 insertions(+), 278 deletions(-) diff --git a/context/AlertContext.tsx b/context/AlertContext.tsx index ed14208..5a027bd 100644 --- a/context/AlertContext.tsx +++ b/context/AlertContext.tsx @@ -4,9 +4,8 @@ import { usePublicClient } from "wagmi"; const DEFAULT_ALERT_TIMEOUT = 7 * 1000; -export type NewAlert = { - type: "success" | "info" | "error"; - message: string; +export type AlertOptions = { + type?: "success" | "info" | "error"; description?: string; txHash?: string; timeout?: number; @@ -14,10 +13,7 @@ export type NewAlert = { export interface AlertContextProps { alerts: IAlert[]; - addAlert: (newAlert: NewAlert) => void; - addSuccessAlert: (message: string) => void; - addInfoAlert: (message: string) => void; - addErrorAlert: (message: string) => void; + addAlert: (message: string, alertOptions?: AlertOptions) => void; } export const AlertContext = createContext( @@ -31,52 +27,43 @@ export const AlertProvider: React.FC<{ children: React.ReactNode }> = ({ const client = usePublicClient(); // Add a new alert to the list - const addAlert = (alert: NewAlert) => { + const addAlert = (message: string, alertOptions?: AlertOptions) => { // Clean duplicates - const idx = alerts.findIndex( - (a) => a.message === alert.message && a.description === alert.description - ); + const idx = alerts.findIndex((a) => { + if (a.message !== message) return false; + else if (a.description !== alertOptions?.description) return false; + else if (a.type !== alertOptions?.type) return false; + + return true; + }); if (idx >= 0) removeAlert(idx); const newAlert: IAlert = { id: Date.now(), - message: alert.message, - description: alert.description, - type: alert.type, + message, + description: alertOptions?.description, + type: alertOptions?.type ?? "info", }; - if (alert.txHash && client) { + if (alertOptions?.txHash && client) { newAlert.explorerLink = - client.chain.blockExplorers?.default.url + "/tx/" + alert.txHash; + client.chain.blockExplorers?.default.url + "/tx/" + alertOptions.txHash; } setAlerts(alerts.concat(newAlert)); // Schedule the clean-up - const timeout = alert.timeout ?? DEFAULT_ALERT_TIMEOUT; + const timeout = alertOptions?.timeout ?? DEFAULT_ALERT_TIMEOUT; setTimeout(() => { removeAlert(newAlert.id); }, timeout); }; - // Convenience aliases - const addSuccessAlert = (message: string) => { - addAlert({ message, type: "success" }); - }; - const addInfoAlert = (message: string) => { - addAlert({ message, type: "info" }); - }; - const addErrorAlert = (message: string) => { - addAlert({ message, type: "error" }); - }; - // Function to remove an alert const removeAlert = (id: number) => { setAlerts(alerts.filter((alert) => alert.id !== id)); }; return ( - + {children} ); diff --git a/plugins/dualGovernance/components/proposal/header.tsx b/plugins/dualGovernance/components/proposal/header.tsx index b6f312b..7081b1a 100644 --- a/plugins/dualGovernance/components/proposal/header.tsx +++ b/plugins/dualGovernance/components/proposal/header.tsx @@ -30,7 +30,7 @@ const ProposalHeader: React.FC = ({ onVetoPressed, }) => { const { reload } = useRouter(); - const { addAlert, addErrorAlert } = useAlertContext() as AlertContextProps; + const { addAlert } = useAlertContext() as AlertContextProps; const proposalVariant = useProposalVariantStatus(proposal); const { @@ -56,14 +56,12 @@ const ProposalHeader: React.FC = ({ if (status === "idle" || status === "pending") return; else if (status === "error") { if (error?.message?.startsWith("User rejected the request")) { - addAlert({ - message: "Transaction rejected by the user", - type: "error", + addAlert("Transaction rejected by the user", { timeout: 4 * 1000, }); } else { console.error(error); - addErrorAlert("Could not execute the proposal"); + addAlert("Could not execute the proposal", { type: "error" }); } return; } @@ -71,8 +69,7 @@ const ProposalHeader: React.FC = ({ // success if (!executeTxHash) return; else if (isConfirming) { - addAlert({ - message: "Proposal submitted", + addAlert("Proposal submitted", { description: "Waiting for the transaction to be validated", type: "info", txHash: executeTxHash, @@ -80,8 +77,7 @@ const ProposalHeader: React.FC = ({ return; } else if (!isConfirmed) return; - addAlert({ - message: "Proposal executed", + addAlert("Proposal executed", { description: "The transaction has been validated", type: "success", txHash: executeTxHash, diff --git a/plugins/dualGovernance/pages/new.tsx b/plugins/dualGovernance/pages/new.tsx index 39af35f..132dc71 100644 --- a/plugins/dualGovernance/pages/new.tsx +++ b/plugins/dualGovernance/pages/new.tsx @@ -42,7 +42,7 @@ export default function Create() { const [title, setTitle] = useState(""); const [summary, setSummary] = useState(""); const [actions, setActions] = useState([]); - const { addAlert, addErrorAlert } = useAlertContext(); + const { addAlert } = useAlertContext(); const { writeContract: createProposalWrite, data: createTxHash, @@ -64,13 +64,11 @@ export default function Create() { if (status === "idle" || status === "pending") return; else if (status === "error") { if (error?.message?.startsWith("User rejected the request")) { - addAlert({ - message: "Transaction rejected by the user", - type: "error", + addAlert("Transaction rejected by the user", { timeout: 4 * 1000, }); } else { - addErrorAlert("Could not create the proposal"); + addAlert("Could not create the proposal", { type: "error" }); } return; } @@ -78,17 +76,14 @@ export default function Create() { // success if (!createTxHash) return; else if (isConfirming) { - addAlert({ - message: "Proposal submitted", + addAlert("Proposal submitted", { description: "Waiting for the transaction to be validated", - type: "info", txHash: createTxHash, }); return; } else if (!isConfirmed) return; - addAlert({ - message: "Proposal created", + addAlert("Proposal created", { description: "The transaction has been validated", type: "success", txHash: createTxHash, @@ -100,13 +95,14 @@ export default function Create() { const submitProposal = async () => { // Check metadata - if (!title.trim()) return addErrorAlert("Please, enter a title"); + if (!title.trim()) + return addAlert("Please, enter a title", { type: "error" }); const plainSummary = getPlainText(summary).trim(); if (!plainSummary.trim()) - return addErrorAlert( - "Please, enter a summary of what the proposal is about" - ); + return addAlert("Please, enter a summary of what the proposal is about", { + type: "error", + }); // Check the action switch (actionType) { @@ -114,15 +110,17 @@ export default function Create() { break; case ActionType.Withdrawal: if (!actions.length) { - return addErrorAlert( - "Please ensure that the withdrawal address and the amount to transfer are valid" + return addAlert( + "Please ensure that the withdrawal address and the amount to transfer are valid", + { type: "error" } ); } break; default: if (!actions.length || !actions[0].data || actions[0].data === "0x") { - return addErrorAlert( - "Please ensure that the values of the action to execute are correct" + return addAlert( + "Please ensure that the values of the action to execute are correct", + { type: "error" } ); } } diff --git a/plugins/dualGovernance/pages/proposal.tsx b/plugins/dualGovernance/pages/proposal.tsx index 09c1025..e6a612f 100644 --- a/plugins/dualGovernance/pages/proposal.tsx +++ b/plugins/dualGovernance/pages/proposal.tsx @@ -44,7 +44,7 @@ export default function ProposalDetail({ id: proposalId }: { id: string }) { const [bottomSection, setBottomSection] = useState("description"); - const { addAlert, addErrorAlert } = useAlertContext() as AlertContextProps; + const { addAlert } = useAlertContext() as AlertContextProps; const { writeContract: vetoWrite, data: vetoTxHash, @@ -58,13 +58,11 @@ export default function ProposalDetail({ id: proposalId }: { id: string }) { if (status === "idle" || status === "pending") return; else if (status === "error") { if (error?.message?.startsWith("User rejected the request")) { - addAlert({ - message: "Transaction rejected by the user", - type: "error", + addAlert("Transaction rejected by the user", { timeout: 4 * 1000, }); } else { - addErrorAlert("Could not create the proposal"); + addAlert("Could not create the proposal", { type: "error" }); } return; } @@ -72,17 +70,14 @@ export default function ProposalDetail({ id: proposalId }: { id: string }) { // success if (!vetoTxHash) return; else if (isConfirming) { - addAlert({ - message: "Veto submitted", + addAlert("Veto submitted", { description: "Waiting for the transaction to be validated", - type: "info", txHash: vetoTxHash, }); return; } else if (!isConfirmed) return; - addAlert({ - message: "Veto registered", + addAlert("Veto registered", { description: "The transaction has been validated", type: "success", txHash: vetoTxHash, diff --git a/plugins/tokenVoting/pages/new.tsx b/plugins/tokenVoting/pages/new.tsx index 78659a8..96f2b60 100644 --- a/plugins/tokenVoting/pages/new.tsx +++ b/plugins/tokenVoting/pages/new.tsx @@ -1,217 +1,263 @@ -import { create } from 'ipfs-http-client'; -import { Button, IconType, Icon, InputText, TextAreaRichText } from '@aragon/ods' -import React, { useEffect, useState } from 'react' -import { uploadToIPFS } from '@/utils/ipfs' -import { useWaitForTransactionReceipt, useWriteContract } from 'wagmi'; -import { toHex } from 'viem' -import { TokenVotingAbi } from '@/plugins/tokenVoting/artifacts/TokenVoting.sol'; -import { useAlertContext } from '@/context/AlertContext'; -import WithdrawalInput from '@/components/input/withdrawal' -import CustomActionInput from '@/components/input/custom-action' -import { Action } from '@/utils/types' -import { getPlainText } from '@/utils/html'; -import { useRouter } from 'next/router'; -import { Else, IfCase, Then } from '@/components/if'; -import { PleaseWaitSpinner } from '@/components/please-wait'; +import { create } from "ipfs-http-client"; import { - PUB_IPFS_API_KEY, - PUB_IPFS_ENDPOINT, - PUB_TOKEN_VOTING_PLUGIN_ADDRESS -} from '@/constants'; + Button, + IconType, + Icon, + InputText, + TextAreaRichText, +} from "@aragon/ods"; +import React, { useEffect, useState } from "react"; +import { uploadToIPFS } from "@/utils/ipfs"; +import { useWaitForTransactionReceipt, useWriteContract } from "wagmi"; +import { toHex } from "viem"; +import { TokenVotingAbi } from "@/plugins/tokenVoting/artifacts/TokenVoting.sol"; +import { useAlertContext } from "@/context/AlertContext"; +import WithdrawalInput from "@/components/input/withdrawal"; +import CustomActionInput from "@/components/input/custom-action"; +import { Action } from "@/utils/types"; +import { getPlainText } from "@/utils/html"; +import { useRouter } from "next/router"; +import { Else, IfCase, Then } from "@/components/if"; +import { PleaseWaitSpinner } from "@/components/please-wait"; +import { + PUB_IPFS_API_KEY, + PUB_IPFS_ENDPOINT, + PUB_TOKEN_VOTING_PLUGIN_ADDRESS, +} from "@/constants"; enum ActionType { - Signaling, - Withdrawal, - Custom + Signaling, + Withdrawal, + Custom, } const ipfsClient = create({ - url: PUB_IPFS_ENDPOINT, - headers: { 'X-API-KEY': PUB_IPFS_API_KEY, 'Accept': 'application/json' } + url: PUB_IPFS_ENDPOINT, + headers: { "X-API-KEY": PUB_IPFS_API_KEY, Accept: "application/json" }, }); export default function Create() { - const { push } = useRouter() - const [title, setTitle] = useState(''); - const [summary, setSummary] = useState(''); - const [actions, setActions] = useState([]); - const { addAlert, addErrorAlert } = useAlertContext() - const { - writeContract: createProposalWrite, - data: createTxHash, - status, - error - } = useWriteContract(); - const { isLoading: isConfirming, isSuccess: isConfirmed } = - useWaitForTransactionReceipt({ hash: createTxHash }); - const [actionType, setActionType] = useState(ActionType.Signaling) - - const changeActionType = (actionType: ActionType) => { - setActions([]) - setActionType(actionType) + const { push } = useRouter(); + const [title, setTitle] = useState(""); + const [summary, setSummary] = useState(""); + const [actions, setActions] = useState([]); + const { addAlert } = useAlertContext(); + const { + writeContract: createProposalWrite, + data: createTxHash, + status, + error, + } = useWriteContract(); + const { isLoading: isConfirming, isSuccess: isConfirmed } = + useWaitForTransactionReceipt({ hash: createTxHash }); + const [actionType, setActionType] = useState( + ActionType.Signaling + ); + + const changeActionType = (actionType: ActionType) => { + setActions([]); + setActionType(actionType); + }; + + 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 { + addAlert("Could not create the proposal", { type: "error" }); + } + return; } - useEffect(() => { - if (status === "idle" || status === "pending") return; - else if (status === "error") { - if (error?.message?.startsWith("User rejected the request")) { - addAlert({ - message: "Transaction rejected by the user", - type: "error", - timeout: 4 * 1000, - }); - } else { - addErrorAlert("Could not create the proposal"); - } - return; + // success + if (!createTxHash) return; + else if (isConfirming) { + addAlert("Proposal submitted", { + description: "Waiting for the transaction to be validated", + txHash: createTxHash, + }); + return; + } else if (!isConfirmed) return; + + addAlert("Proposal created", { + description: "The transaction has been validated", + type: "success", + txHash: createTxHash, + }); + + setTimeout(() => { + push("#/"); + }, 1000 * 2); + }, [status, createTxHash, isConfirming, isConfirmed]); + + const submitProposal = async () => { + // Check metadata + if (!title.trim()) + return addAlert("Please, enter a title", { type: "error" }); + + const plainSummary = getPlainText(summary).trim(); + if (!plainSummary.trim()) + return addAlert("Please, enter a summary of what the proposal is about", { + type: "error", + }); + + // Check the action + switch (actionType) { + case ActionType.Signaling: + break; + case ActionType.Withdrawal: + if (!actions.length) { + return addAlert( + "Please ensure that the withdrawal address and the amount to transfer are valid", + { type: "error" } + ); } - - // success - if (!createTxHash) return; - else if (isConfirming) { - addAlert({ - message: "Proposal submitted", - description: "Waiting for the transaction to be validated", - type: "info", - txHash: createTxHash, - }); - return; - } else if (!isConfirmed) return; - - addAlert({ - message: "Proposal created", - description: "The transaction has been validated", - type: "success", - txHash: createTxHash, - }); - - setTimeout(() => { - push("#/"); - }, 1000 * 2); - }, [status, createTxHash, isConfirming, isConfirmed]); - - const submitProposal = async () => { - // Check metadata - if (!title.trim()) return addErrorAlert("Please, enter a title"); - - const plainSummary = getPlainText(summary).trim() - if (!plainSummary.trim()) return addErrorAlert("Please, enter a summary of what the proposal is about"); - - // Check the action - switch (actionType) { - case ActionType.Signaling: break; - case ActionType.Withdrawal: - if (!actions.length) { - return addErrorAlert("Please ensure that the withdrawal address and the amount to transfer are valid"); - } - break - default: - if (!actions.length || !actions[0].data || actions[0].data === "0x") { - return addErrorAlert("Please ensure that the values of the action to execute are correct"); - } + break; + default: + if (!actions.length || !actions[0].data || actions[0].data === "0x") { + return addAlert( + "Please ensure that the values of the action to execute are correct", + { type: "error" } + ); } - - const proposalMetadataJsonObject = { title, summary }; - const blob = new Blob([JSON.stringify(proposalMetadataJsonObject)], { type: 'application/json' }); - - const ipfsPin = await uploadToIPFS(ipfsClient, blob); - createProposalWrite({ - abi: TokenVotingAbi, - address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS, - functionName: 'createProposal', - args: [toHex(ipfsPin), actions, 0, 0, 0, 0, 0], - }) } - const handleTitleInput = (event: React.ChangeEvent) => { - setTitle(event?.target?.value); - }; - - const showLoading = status === "pending" || isConfirming; - - return ( -
-
-

Create Proposal

-
- -
-
- -
-
- Select proposal action -
-
{changeActionType(ActionType.Signaling)}} - className={`rounded-xl border border-solid border-2 bg-neutral-0 hover:bg-neutral-50 flex flex-col items-center cursor-pointer ${actionType === ActionType.Signaling ? 'border-primary-300' : 'border-neutral-100'}`}> - - Signaling -
-
changeActionType(ActionType.Withdrawal)} - className={`rounded-xl border border-solid border-2 bg-neutral-0 hover:bg-neutral-50 flex flex-col items-center cursor-pointer ${actionType === ActionType.Withdrawal ? 'border-primary-300' : 'border-neutral-100'}`}> - - DAO Payment -
-
changeActionType(ActionType.Custom)} - className={`rounded-xl border border-solid border-2 bg-neutral-0 hover:bg-neutral-50 flex flex-col items-center cursor-pointer ${actionType === ActionType.Custom ? 'border-primary-300' : 'border-neutral-100'}`}> - - Custom action -
-
-
- {actionType === ActionType.Withdrawal && ()} - {actionType === ActionType.Custom && ()} -
-
- - - -
- -
-
- - - -
+ const proposalMetadataJsonObject = { title, summary }; + const blob = new Blob([JSON.stringify(proposalMetadataJsonObject)], { + type: "application/json", + }); + + const ipfsPin = await uploadToIPFS(ipfsClient, blob); + createProposalWrite({ + abi: TokenVotingAbi, + address: PUB_TOKEN_VOTING_PLUGIN_ADDRESS, + functionName: "createProposal", + args: [toHex(ipfsPin), actions, 0, 0, 0, 0, 0], + }); + }; + + const handleTitleInput = (event: React.ChangeEvent) => { + setTitle(event?.target?.value); + }; + + const showLoading = status === "pending" || isConfirming; + + return ( +
+
+

+ Create Proposal +

+
+ +
+
+ +
+
+ + Select proposal action + +
+
{ + changeActionType(ActionType.Signaling); + }} + className={`rounded-xl border border-solid border-2 bg-neutral-0 hover:bg-neutral-50 flex flex-col items-center cursor-pointer ${ + actionType === ActionType.Signaling + ? "border-primary-300" + : "border-neutral-100" + }`} + > + + + Signaling +
-
- ) -} +
changeActionType(ActionType.Withdrawal)} + className={`rounded-xl border border-solid border-2 bg-neutral-0 hover:bg-neutral-50 flex flex-col items-center cursor-pointer ${ + actionType === ActionType.Withdrawal + ? "border-primary-300" + : "border-neutral-100" + }`} + > + + + DAO Payment + +
+
changeActionType(ActionType.Custom)} + className={`rounded-xl border border-solid border-2 bg-neutral-0 hover:bg-neutral-50 flex flex-col items-center cursor-pointer ${ + actionType === ActionType.Custom + ? "border-primary-300" + : "border-neutral-100" + }`} + > + + + Custom action + +
+
+
+ {actionType === ActionType.Withdrawal && ( + + )} + {actionType === ActionType.Custom && ( + + )} +
+ + + +
+ +
+
+ + + +
+ +
+ ); +} diff --git a/plugins/tokenVoting/pages/proposal.tsx b/plugins/tokenVoting/pages/proposal.tsx index a6c8f6b..85bf7ca 100644 --- a/plugins/tokenVoting/pages/proposal.tsx +++ b/plugins/tokenVoting/pages/proposal.tsx @@ -54,7 +54,7 @@ export default function ProposalDetail({ id: proposalId }: { id: string }) { const [votedOption, setVotedOption] = useState(undefined); const [showVotingModal, setShowVotingModal] = useState(false); const [selectedVoteOption, setSelectedVoteOption] = useState(); - const { addAlert, addErrorAlert } = useAlertContext() as AlertContextProps; + const { addAlert } = useAlertContext() as AlertContextProps; const { address } = useAccount(); const { writeContract: voteWrite, @@ -100,13 +100,11 @@ export default function ProposalDetail({ id: proposalId }: { id: string }) { if (status === "idle" || status === "pending") return; else if (status === "error") { if (error?.message?.startsWith("User rejected the request")) { - addAlert({ - message: "Transaction rejected by the user", - type: "error", + addAlert("Transaction rejected by the user", { timeout: 4 * 1000, }); } else { - addErrorAlert("Could not create the proposal"); + addAlert("Could not create the proposal", { type: "error" }); } return; } @@ -114,17 +112,14 @@ export default function ProposalDetail({ id: proposalId }: { id: string }) { // success if (!votingTxHash) return; else if (isConfirming) { - addAlert({ - message: "Vote submitted", + addAlert("Vote submitted", { description: "Waiting for the transaction to be validated", - type: "info", txHash: votingTxHash, }); return; } else if (!isConfirmed) return; - addAlert({ - message: "Vote registered", + addAlert("Vote registered", { description: "The transaction has been validated", type: "success", txHash: votingTxHash,