Skip to content

Commit

Permalink
Ad unique ids, use them when adding/removing methods
Browse files Browse the repository at this point in the history
  • Loading branch information
portdeveloper committed Mar 9, 2024
1 parent 4d72834 commit f8f9204
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export const ContractReadMethods = ({
}

const functionsToDisplay = (
((deployedContractData.abi || []) as Abi).filter(part => part.type === "function") as AbiFunction[]
((deployedContractData.abi || []) as Abi).filter(part => part.type === "function") as (AbiFunction & {
uid: string;
})[]
)
.filter(fn => {
const isQueryableWithParams =
Expand All @@ -41,15 +43,15 @@ export const ContractReadMethods = ({
return (
<>
{functionsToDisplay.map(({ fn, inheritedFrom }) => (
<div key={fn.name} className="relative mb-4 pt-5">
<div key={fn.uid} className="relative mb-4 pt-5">
<ReadOnlyFunctionForm
abi={deployedContractData.abi as Abi}
contractAddress={deployedContractData.address}
abiFunction={fn}
inheritedFrom={inheritedFrom}
/>
<button
onClick={() => removeMethod(fn.name)}
onClick={() => removeMethod(fn.uid)}
className="absolute top-0 right-0 btn btn-ghost btn-xs mt-[21px]"
aria-label="Remove method"
>
Expand Down
62 changes: 36 additions & 26 deletions packages/nextjs/components/scaffold-eth/Contract/ContractUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type ContractUIProps = {
initialContractData: { address: string; abi: Abi };
};

interface AugmentedAbiFunction extends AbiFunction {
uid: string;
}

const mainNetworks = getTargetNetworks();

/**
Expand All @@ -43,48 +47,54 @@ export const ContractUI = ({ className = "", initialContractData }: ContractUIPr
router.push({ pathname: newPath, query: currentQuery.toString() }, undefined, { shallow: true });
};

const readMethodsWithInputsAndWriteMethods = initialContractData.abi.filter((method): method is AbiFunction => {
if (method.type !== "function") return false;
const augmentMethodsWithUid = (methods: AbiFunction[]): AugmentedAbiFunction[] => {
return methods.map((method, index) => ({
...method,
uid: `${method.name}_${index}`, // Simple UID based on index
}));
};

const readMethodsWithInputsAndWriteMethods = augmentMethodsWithUid(
initialContractData.abi.filter((method): method is AbiFunction => {
if (method.type !== "function") return false;

// Check for read functions
if (method.stateMutability === "view" || method.stateMutability === "pure") {
// Check for read inputs length
if (method.inputs.length > 0) {
// Check for read functions
if (method.stateMutability === "view" || method.stateMutability === "pure") {
// Check for read inputs length
return method.inputs.length > 0;
} else {
// Else condition defines write methods
return true;
} else return false;
} else {
// Else condition defines write methods
return true;
}
});
}
}),
);

// local abi state for for dispalying selected methods
const [abi, setAbi] = useState<AbiFunction[]>([]);
const [abi, setAbi] = useState<(AbiFunction & { uid: string })[]>([]);

const handleMethodSelect = (methodName: string) => {
const methodToAdd = initialContractData.abi.find(
method => method.type === "function" && "name" in method && method.name === methodName,
) as AbiFunction | undefined; // Cast it to AbiFunction | undefined
const handleMethodSelect = (uid: string) => {
const methodToAdd = readMethodsWithInputsAndWriteMethods.find(method => method.uid === uid);

if (methodToAdd && !abi.some(method => method.name === methodName)) {
if (methodToAdd && !abi.some(method => method.uid === uid)) {
const updatedAbi = [...abi, methodToAdd];
setAbi(updatedAbi);
updateUrlWithSelectedMethods(updatedAbi.map(m => m.name));
updateUrlWithSelectedMethods(updatedAbi.map(m => m.uid));
}
};

const removeMethod = (methodName: string) => {
const updatedAbi = abi.filter(fn => fn.name !== methodName);
const removeMethod = (uid: string) => {
const updatedAbi = abi.filter(method => method.uid !== uid);

setAbi(updatedAbi);
updateUrlWithSelectedMethods(updatedAbi.map(m => m.name));
updateUrlWithSelectedMethods(updatedAbi.map(m => m.uid));
};

useEffect(() => {
const selectedMethodNames = (router.query.methods as string)?.split(",") || [];
const selectedMethods = initialContractData.abi.filter(
method => method.type === "function" && "name" in method && selectedMethodNames.includes(method.name),
) as AbiFunction[]; // Cast it to AbiFunction[]
setAbi(selectedMethods);
const selectedMethods = readMethodsWithInputsAndWriteMethods.filter(
method => method.type === "function" && "name" in method && selectedMethodNames.includes(method.uid),
) as AbiFunction[];
setAbi(selectedMethods as (AbiFunction & { uid: string })[]);
}, [initialContractData.abi, router?.query?.methods]);

const { data: contractNameData, isLoading: isContractNameLoading } = useContractRead({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const ContractWriteMethods = ({
}

const functionsToDisplay = (
(deployedContractData.abi as Abi).filter(part => part.type === "function") as AbiFunction[]
(deployedContractData.abi as Abi).filter(part => part.type === "function") as (AbiFunction & { uid: string })[]
)
.filter(fn => {
const isWriteableFunction = fn.stateMutability !== "view" && fn.stateMutability !== "pure";
Expand Down Expand Up @@ -51,7 +51,7 @@ export const ContractWriteMethods = ({
inheritedFrom={inheritedFrom}
/>
<button
onClick={() => removeMethod(fn.name)}
onClick={() => removeMethod(fn.uid)}
className="absolute top-0 right-0 btn btn-ghost btn-xs mt-[21px]"
aria-label="Remove method"
>
Expand Down
39 changes: 20 additions & 19 deletions packages/nextjs/components/scaffold-eth/Contract/MethodSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useState } from "react";
import { AbiFunction } from "abitype";
import { Abi } from "viem";
import { ChevronDownIcon, ChevronRightIcon, XMarkIcon } from "@heroicons/react/24/outline";

interface MethodSelectorProps {
readMethodsWithInputsAndWriteMethods: AbiFunction[];
abi: Abi;
onMethodSelect: (selectedMethods: string) => void;
removeMethod: (methodName: string) => void;
readMethodsWithInputsAndWriteMethods: (AbiFunction & { uid: string })[];
abi: (AbiFunction & { uid: string })[];
onMethodSelect: (uid: string) => void; // Changed to accept UID
removeMethod: (uid: string) => void; // Changed to accept UID
}

export const MethodSelector = ({
Expand All @@ -20,19 +19,21 @@ export const MethodSelector = ({
const [isWriteCollapsed, setIsWriteCollapsed] = useState(false);

const readMethods = readMethodsWithInputsAndWriteMethods.filter(
(method): method is AbiFunction => method.stateMutability === "view" || method.stateMutability === "pure",
method => method.stateMutability === "view" || method.stateMutability === "pure",
);

const writeMethods = readMethodsWithInputsAndWriteMethods.filter(
(method): method is AbiFunction => method.stateMutability !== "view" && method.stateMutability !== "pure",
method => method.stateMutability !== "view" && method.stateMutability !== "pure",
);

const handleMethodSelect = (methodName: string) => {
onMethodSelect(methodName);
const handleMethodSelection = (uid: string) => {
console.log("handleMethodSelection", uid);
onMethodSelect(uid);
};

const isMethodSelected = (methodName: string) => {
return abi.some(method => "name" in method && method.name === methodName);
// Check if a method is selected by UID
const isMethodSelected = (uid: string) => {
return abi.some(method => method.uid === uid);
};

return (
Expand All @@ -57,19 +58,19 @@ export const MethodSelector = ({
</h3>
{!isReadCollapsed && (
<div className="flex flex-col items-start gap-1 pb-4">
{readMethods.map((method, index) => (
<div key={index} className="flex items-center gap-2 w-full pr-4">
{readMethods.map(method => (
<div key={method.uid} className="flex items-center gap-2 w-full pr-4">
<button
className={`btn btn-sm btn-ghost font-normal pr-1 w-full justify-between ${
isMethodSelected(method.name) ? "bg-purple-100 pointer-events-none" : ""
isMethodSelected(method.uid) ? "bg-purple-100 pointer-events-none" : ""
}`}
onClick={() => handleMethodSelect(method.name)}
onClick={() => handleMethodSelection(method.uid)}
>
{method.name}
{isMethodSelected(method.name) && (
{isMethodSelected(method.uid) && (
<button
className="ml-4 text-xs hover:bg-gray-100 rounded-md p-1 pointer-events-auto"
onClick={() => removeMethod(method.name)}
onClick={() => removeMethod(method.uid)}
>
<XMarkIcon className="h-4 w-4" />
</button>
Expand Down Expand Up @@ -102,13 +103,13 @@ export const MethodSelector = ({
className={`btn btn-sm btn-ghost font-normal pr-1 w-full justify-between ${
isMethodSelected(method.name) ? "bg-purple-100 pointer-events-none" : ""
}`}
onClick={() => handleMethodSelect(method.name)}
onClick={() => handleMethodSelection(method.uid)}
>
{method.name}
{isMethodSelected(method.name) && (
<button
className="ml-4 text-xs hover:bg-gray-100 rounded-md p-1 pointer-events-auto"
onClick={() => removeMethod(method.name)}
onClick={() => removeMethod(method.uid)}
>
<XMarkIcon className="h-4 w-4" />
</button>
Expand Down

0 comments on commit f8f9204

Please sign in to comment.