Skip to content

Commit

Permalink
Merge pull request #48 from dwhiffing/toggle-contracts
Browse files Browse the repository at this point in the history
Toggle contracts
  • Loading branch information
dwhiffing authored Sep 9, 2024
2 parents 7bb2739 + 5609030 commit 664315d
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 43 deletions.
4 changes: 3 additions & 1 deletion app/api/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export async function POST(req: NextRequest) {
try {
const body = await req.json();
const messages = body.messages ?? [];
const contracts = await contractCollection.get();
const contracts = (await contractCollection.get()).filter(
(c) => !(body.disabledContractKeys ?? []).includes(c.key),
);
const formattedPreviousMessages = messages.slice(0, -1);
const currentMessageContent = messages[messages.length - 1].content;
const contractAddresses = contracts.map(({ address }) => address);
Expand Down
4 changes: 3 additions & 1 deletion app/api/execute/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export async function POST(req: NextRequest) {

// parse contractAddress from toolCall.name; Should be in format `${contractKey}_${functionName}_${overload function index}``
const contractKey = parseInt(toolCall.name.split("_").at(0) as string, 10);
const contracts = await contractCollection.get();
const contracts = (await contractCollection.get()).filter(
(c) => !(body.disabledContractKeys ?? []).includes(c.key),
);
const contract = contracts.find(({ key }) => contractKey === key);

if (!contract) {
Expand Down
6 changes: 2 additions & 4 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import { ChatWindow } from "@/components/ChatWindow";
import { useMagic } from "@/components/MagicProvider";
import { LoadingIcon } from "@/components/LoadingIcon";
import { Button } from "@/components/ui/button";
import {
ContractItem,
UploadContractModal,
} from "@/components/UploadContractModal";
import { UploadContractModal } from "@/components/UploadContractModal";
import { ContractItem } from "@/components/ContractItem";
import { useContracts } from "@/utils/useContracts";
import { ScrollArea } from "@/components/ui/scroll-area";
import { EditContractModal } from "@/components/EditContractModal";
Expand Down
4 changes: 2 additions & 2 deletions components/ChatMessageBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,7 @@ export function ToolCallMessageBubble(props: { message: Message }) {
useState<IToolCallResponse | null>(null);
const [loading, setLoading] = useState(false);
const { didToken } = useMagic();
const { contracts } = useContracts();

const { contracts, disabledKeys } = useContracts();
const { colorClassName, alignmentClassName, icon } = getStyleForRole(
props.message.role,
);
Expand All @@ -139,6 +138,7 @@ export function ToolCallMessageBubble(props: { message: Message }) {
body: JSON.stringify({
toolCall,
didToken,
disabledContractKeys: disabledKeys,
}),
});

Expand Down
4 changes: 3 additions & 1 deletion components/ChatWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ import { LoadingIcon } from "@/components/LoadingIcon";
import { Label } from "./ui/label";
import { CornerDownLeft, Trash2 } from "lucide-react";
import { ConfirmAlert } from "./ConfirmAlert";
import { useContracts } from "@/utils/useContracts";

export function ChatWindow(props: { titleText?: string }) {
const { titleText } = props;
const chatContainerRef = useRef<HTMLDivElement>(null);

const { disabledKeys } = useContracts();
const {
messages,
input,
Expand All @@ -32,6 +33,7 @@ export function ChatWindow(props: { titleText?: string }) {
isLoading,
} = useChat({
api: "api/chat",
body: { disabledContractKeys: disabledKeys },
streamProtocol: "text",
onError: (e) => {
toast(e.message);
Expand Down
56 changes: 56 additions & 0 deletions components/ContractItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Pencil, Circle, CircleCheck } from "lucide-react";
import { CHAINS } from "@/constants";
import { IContract } from "@/types";
import { shortenAddress } from "../utils/shortenAddress";
import { useContracts } from "@/utils/useContracts";

export const ContractItem = (props: {
contract: IContract;
onEdit?: (key: number) => void;
}) => {
const { disabledKeys, setDisabledKeys } = useContracts();
const isDisabled = disabledKeys.includes(props.contract.key);
const DisabledIcon = isDisabled ? Circle : CircleCheck;
return (
<div className="border p-3 rounded-md">
<div
className={`flex items-center gap-2 ${isDisabled ? "opacity-50" : ""}`}
>
<div className="flex flex-col flex-1">
<span>
{props.contract.name}{" "}
<small className="text-muted-foreground">
({CHAINS[props.contract.chainId]?.name})
</small>
</span>
<small className="font-xs font-mono text-muted-foreground">
{shortenAddress(props.contract.address)}
</small>
{props.contract.description && (
<small className="font-xs text-muted-foreground mt-1">
{props.contract.description}
</small>
)}
</div>

{props.onEdit && (
<Pencil
onClick={() => props.onEdit?.(props.contract.key)}
className="h-4 w-4 cursor-pointer"
/>
)}

<DisabledIcon
className="h-4 w-4 cursor-pointer"
onClick={() =>
isDisabled
? setDisabledKeys(
disabledKeys.filter((k) => k !== props.contract.key),
)
: setDisabledKeys([...disabledKeys, props.contract.key])
}
/>
</div>
</div>
);
};
34 changes: 1 addition & 33 deletions components/UploadContractModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
DialogTitle,
} from "./ui/dialog";
import { Label } from "./ui/label";
import { Pencil } from "lucide-react";
import {
Select,
SelectContent,
Expand All @@ -20,9 +19,8 @@ import {
SelectValue,
} from "./ui/select";
import { CHAINS } from "@/constants";
import { IContract, ChainIdEnum } from "@/types";
import { ChainIdEnum } from "@/types";
import { useContracts } from "../utils/useContracts";
import { shortenAddress } from "../utils/shortenAddress";
import { Textarea } from "./ui/textarea";

export function UploadContractModal({
Expand Down Expand Up @@ -139,36 +137,6 @@ export function UploadContractModal({
);
}

export const ContractItem = (props: {
contract: IContract;
onEdit?: (key: number) => void;
}) => (
<div className="flex items-center gap-2 border p-3 rounded-md">
<div className="flex flex-col flex-1">
<span>
{props.contract.name}{" "}
<small className="text-muted-foreground">
({CHAINS[props.contract.chainId]?.name})
</small>
</span>
<small className="font-xs font-mono text-muted-foreground">
{shortenAddress(props.contract.address)}
</small>
{props.contract.description && (
<small className="font-xs text-muted-foreground mt-1">
{props.contract.description}
</small>
)}
</div>
{props.contract.key > -1 && props.onEdit && (
<Pencil
onClick={() => props.onEdit?.(props.contract.key!)}
className="h-4 w-4 cursor-pointer"
/>
)}
</div>
);

const ChainSelect = (props: {
chainId: ChainIdEnum | -1;
setChainId: (chainId: ChainIdEnum) => void;
Expand Down
2 changes: 1 addition & 1 deletion utils/generateToolFromABI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ const getToolFunction =
// Just to get around TS
if (error instanceof TransactionError) {
console.error(`${error.constructor.name}:`, error.message);
const transactionHash = error.context.hash;
const transactionHash = error.context?.hash;
return JSON.stringify({
message: error.message,
status: "failure",
Expand Down
7 changes: 7 additions & 0 deletions utils/useContracts.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import useSWR from "swr";
import { IABIFunctionDescription, IContract } from "@/types";
import { useState } from "react";
import useLocalStorage from "./useLocalStorage";

export const useContracts = () => {
const [erroMessage, setErrorMessage] = useState("");
const [disabledKeys, setDisabledKeys] = useLocalStorage<number[]>(
"disabled-contracts",
[],
);
const {
data: contracts = [],
error,
Expand Down Expand Up @@ -91,6 +96,8 @@ export const useContracts = () => {
};

return {
disabledKeys,
setDisabledKeys,
contracts,
isLoading: !error && !contracts,
errorMessage: erroMessage || error?.message || "",
Expand Down
46 changes: 46 additions & 0 deletions utils/useLocalStorage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useEffect, useState } from "react";

export default function useLocalStorage<T>(
key: string,
defaultValue: T,
): [T, (value: T) => void] {
const [value, setValue] = useState(defaultValue);

useEffect(() => {
const item = localStorage.getItem(key);

if (!item) {
localStorage.setItem(key, JSON.stringify(defaultValue));
}

setValue(item ? JSON.parse(item) : defaultValue);

function handler(e: StorageEvent) {
if (e.key !== key) return;

const lsi = localStorage.getItem(key);
setValue(JSON.parse(lsi ?? ""));
}

window.addEventListener("storage", handler);

return () => {
window.removeEventListener("storage", handler);
};
}, []);

const setValueWrap = (value: T) => {
try {
setValue(value);

localStorage.setItem(key, JSON.stringify(value));
if (typeof window !== "undefined") {
window.dispatchEvent(new StorageEvent("storage", { key }));
}
} catch (e) {
console.error(e);
}
};

return [value, setValueWrap];
}

0 comments on commit 664315d

Please sign in to comment.