diff --git a/src/app/NewSafeProposal.tsx b/src/app/NewSafeProposal.tsx index 0d2a061..5a2eb0c 100644 --- a/src/app/NewSafeProposal.tsx +++ b/src/app/NewSafeProposal.tsx @@ -1,35 +1,26 @@ import { Field, FieldArray, Formik } from "formik"; import { SafeInformation } from "../components/SafeInformation"; import { Card, View, Text, Button, useToast } from "reshaped"; -import { - SyntheticEvent, - createContext, - useCallback, - useEffect, - useState, -} from "react"; +import { SyntheticEvent, useCallback, useEffect, useState } from "react"; import { Address, Hex, formatEther } from "viem"; import { validateAddress, validateETH } from "../utils/validators"; import { GenericField } from "../components/GenericField"; import { DataActionPreview } from "../components/DataActionPreview"; -import Safe, { EthersAdapter } from "@safe-global/protocol-kit"; -import { ethers } from "ethers"; -import { contractNetworks } from "../chains"; +import Safe from "@safe-global/protocol-kit"; import { DEFAULT_ACTION_ITEM, DEFAULT_PROPOSAL, Proposal, proposalSchema, } from "../schemas/proposal"; -import { useRedirectToProposalWithNewParams } from "../hooks/useSetParamsFromQuery"; import { useLoadProposalFromQuery } from "../hooks/useLoadProposalFromQuery"; import { transformValuesFromWei, transformValuesToWei, } from "../utils/etherFormatting"; -import { BrowserProvider } from "ethers"; import { useOutletContext } from "react-router-dom"; import { NetworkContext, SafeContext } from "../components/Contexts"; +import { useUpdateProposal } from "../hooks/useUpdateProposalViaQuery"; const FormActionItem = ({ name, @@ -62,24 +53,6 @@ const FormActionItem = ({ ); }; -const createSafeAdapter = async ({ - provider, - safeAddress, -}: { - provider: BrowserProvider; - safeAddress: Address; -}) => { - const ethAdapter = new EthersAdapter({ - ethers, - signerOrProvider: await provider!.getSigner(), - }); - return await Safe.create({ - ethAdapter, - safeAddress, - contractNetworks, - }); -}; - const createSafeTransaction = async ({ proposal, safe, @@ -335,20 +308,19 @@ const ViewProposal = ({ const EditProposal = ({ proposal, + setProposal, }: { proposal: Proposal | undefined; - setProposal: (result: Proposal) => void; - setIsEditing: (editing: boolean) => void; + setProposal: (proposal: Proposal) => void; }) => { - const redirectToEditedProposal = useRedirectToProposalWithNewParams(); const onSubmit = useCallback( (result: Proposal) => { const proposal = transformValuesToWei(result); if (proposal) { - redirectToEditedProposal(proposal); + setProposal(proposal); } }, - [redirectToEditedProposal] + [setProposal] ); const defaultActions = proposal || DEFAULT_PROPOSAL; @@ -410,11 +382,9 @@ const EditProposal = ({ ); }; -export const ProposalContext = createContext(undefined); - export const NewSafeProposal = () => { const [proposal, setProposal] = useState( - DEFAULT_PROPOSAL, + DEFAULT_PROPOSAL ); const [isEditing, setIsEditing] = useState(true); @@ -432,28 +402,27 @@ export const NewSafeProposal = () => { setIsEditing(true); evt.preventDefault(); }, - [setIsEditing], + [setIsEditing] ); + const updateProposal = useUpdateProposal({ proposal }); + return ( - - - - {isEditing && ( - - )} - {!isEditing && proposal && ( - - )} - - - + + + {isEditing && ( + + )} + {!isEditing && proposal && ( + + )} + + ); }; diff --git a/src/components/SafeInformation.tsx b/src/components/SafeInformation.tsx index 4d2a36b..baf706a 100644 --- a/src/components/SafeInformation.tsx +++ b/src/components/SafeInformation.tsx @@ -7,6 +7,7 @@ import { AddressView } from "../components/AddressView"; import { OwnerAction, SetOwnerModal } from "../components/SetOwnerModal"; import { useOutletContext } from "react-router-dom"; import { SafeContext } from "./Contexts"; +import { UpdateProposal } from "../hooks/useUpdateProposalViaQuery"; const SafeInformationItem = ({ title, @@ -27,8 +28,10 @@ const SafeInformationItem = ({ export const SafeInformation = ({ children, + updateProposal, }: { children?: React.ReactNode; + updateProposal: UpdateProposal; }) => { const [ownerAction, setOwnerAction] = useState(); @@ -42,6 +45,7 @@ export const SafeInformation = ({ setOwnerAction(undefined); }} action={ownerAction} + updatedProposal={updateProposal} /> )} diff --git a/src/components/SetOwnerModal.tsx b/src/components/SetOwnerModal.tsx index 131a7ba..f16f1b6 100644 --- a/src/components/SetOwnerModal.tsx +++ b/src/components/SetOwnerModal.tsx @@ -1,4 +1,4 @@ -import { SyntheticEvent, useContext } from "react"; +import { SyntheticEvent } from "react"; import { Button, Modal, Text, View, useToast } from "reshaped"; import { AddressView } from "./AddressView"; import { Address } from "viem"; @@ -8,8 +8,7 @@ import { yupAddress } from "../utils/validators"; import { number, object } from "yup"; import { useOutletContext } from "react-router-dom"; import { SafeContext } from "./Contexts"; -import { ProposalContext } from "../app/NewSafeProposal"; -import { useUpdateProposalViaQuery } from "../hooks/useUpdateProposalViaQuery"; +import { UpdateProposal } from "../hooks/useUpdateProposalViaQuery"; export type OwnerAction = | undefined @@ -42,11 +41,15 @@ const ButtonPanel = ({ ); -const AddOwnerModalContent = ({ onClose }: { onClose: () => void }) => { +const AddOwnerModalContent = ({ + onClose, + updateProposal: { addAction }, +}: { + onClose: () => void; + updateProposal: UpdateProposal; +}) => { const { safeInformation } = useOutletContext(); const toast = useToast(); - const updateProposalQuery = useUpdateProposalViaQuery(); - const currentProposal = useContext(ProposalContext); return ( void }) => { ownerAddress: address, threshold: threshold, }); - updateProposalQuery({ + addAction({ data: addOwnerTx.data.data, value: "0", to: safeInformation.address, @@ -96,12 +99,13 @@ const AddOwnerModalContent = ({ onClose }: { onClose: () => void }) => { const RemoveOwnerModalContent = ({ onClose, target, + updateProposal: { addAction }, }: { onClose: () => void; target: string; + updateProposal: UpdateProposal; }) => { const { safeInformation } = useOutletContext(); - const updateProposalViaQuery = useUpdateProposalViaQuery(); const toaster = useToast(); const onSubmitClick = async ({ threshold }: any) => { @@ -110,13 +114,13 @@ const RemoveOwnerModalContent = ({ { ownerAddress: target, threshold: threshold, - }, + } ); if (!removeOwnerTx || !safeInformation) { return; } - updateProposalViaQuery({ + addAction({ data: removeOwnerTx.data.data, value: "0", to: safeInformation.address, @@ -158,16 +162,27 @@ const RemoveOwnerModalContent = ({ export const SetOwnerModal = ({ action, onClose, + updatedProposal, }: { action: OwnerAction; onClose: () => void; + updatedProposal: UpdateProposal; }) => { return ( {action?.type === "remove" && ( - + + )} + {action?.type === "add" && ( + )} - {action?.type === "add" && } ); }; diff --git a/src/hooks/useLoadProposalFromQuery.ts b/src/hooks/useLoadProposalFromQuery.ts index b19780c..542637e 100644 --- a/src/hooks/useLoadProposalFromQuery.ts +++ b/src/hooks/useLoadProposalFromQuery.ts @@ -38,7 +38,12 @@ export const useLoadProposalFromQuery = () => { console.log({ actions, txt: "setting proposal" }); - setProposal({ actions, ...(nonce ? { [queryKeys.nonce]: nonce } : {}) }); + const proposal: Proposal = { + actions, + ...(nonce ? { nonce: parseInt(nonce) } : {}), + }; + + setProposal(proposal); } }, [params, setProposal]); diff --git a/src/hooks/useSetParamsFromQuery.ts b/src/hooks/useSetParamsFromQuery.ts index ea6cb4c..b195da2 100644 --- a/src/hooks/useSetParamsFromQuery.ts +++ b/src/hooks/useSetParamsFromQuery.ts @@ -4,7 +4,7 @@ import { Proposal } from "../schemas/proposal"; import { queryKeys } from "./useLoadProposalFromQuery"; export const useRedirectToProposalWithNewParams = () => { - const [_, setParams] = useSearchParams(); + const [, setParams] = useSearchParams(); return useCallback( (proposal: Proposal) => { @@ -12,9 +12,15 @@ export const useRedirectToProposalWithNewParams = () => { return; } setParams({ - [queryKeys.targets]: proposal.actions!.map((action) => action.to).join("|"), - [queryKeys.calldatas]: proposal.actions!.map((action) => action.data).join("|"), - [queryKeys.values]: proposal.actions!.map((action) => action.value).join("|"), + [queryKeys.targets]: proposal + .actions!.map((action) => action.to) + .join("|"), + [queryKeys.calldatas]: proposal + .actions!.map((action) => action.data) + .join("|"), + [queryKeys.values]: proposal + .actions!.map((action) => action.value) + .join("|"), ...(proposal.nonce ? { [queryKeys.nonce]: proposal.nonce.toString(), @@ -22,6 +28,6 @@ export const useRedirectToProposalWithNewParams = () => { : {}), }); }, - [setParams], + [setParams] ); }; diff --git a/src/hooks/useUpdateProposalViaQuery.ts b/src/hooks/useUpdateProposalViaQuery.ts index 4d858a0..15e7b5d 100644 --- a/src/hooks/useUpdateProposalViaQuery.ts +++ b/src/hooks/useUpdateProposalViaQuery.ts @@ -1,19 +1,30 @@ -import { useCallback, useContext } from "react"; +import { useCallback } from "react"; import { Proposal } from "../schemas/proposal"; -import { ProposalContext } from "../app/NewSafeProposal"; import { useRedirectToProposalWithNewParams } from "./useSetParamsFromQuery"; -export const useUpdateProposalViaQuery = () => { +export type UpdateProposal = { + addAction: (newAction: NonNullable[0]) => void; + replace: (proposal: Proposal) => void; +}; +export const useUpdateProposal = ({ + proposal, +}: { + proposal: Proposal | undefined; +}): UpdateProposal => { const setParams = useRedirectToProposalWithNewParams(); - const proposal = useContext(ProposalContext); - return useCallback( + const addAction = useCallback( (newAction: NonNullable[0]) => { setParams({ actions: [...(proposal?.actions || []), newAction], nonce: proposal?.nonce, }); }, - [proposal, setParams], + [proposal, setParams] ); + + return { + addAction, + replace: setParams, + }; };