diff --git a/app/src/AbiApp.tsx b/app/src/AbiApp.tsx new file mode 100644 index 0000000..877138a --- /dev/null +++ b/app/src/AbiApp.tsx @@ -0,0 +1,79 @@ +import { useCallback, useEffect, useState } from "react"; +import LogView from "./features/editor/components/LogView"; +import { loadAbi, saveAbi, saveSwayCode } from "./utils/localStorage"; +import InteractionDrawer from "./features/interact/components/InteractionDrawer"; +import { useLog } from "./features/editor/hooks/useLog"; +import { Analytics } from "@vercel/analytics/react"; +import useTheme from "./context/theme"; +import AbiActionToolbar from "./features/toolbar/components/AbiActionToolbar"; +import AbiEditorView from "./features/editor/components/AbiEditorView"; + +const DRAWER_WIDTH = "40vw"; + +function AbiApp() { + // The current sway code in the editor. + const [abiCode, setAbiCode] = useState(loadAbi()); + + // Functions for reading and writing to the log output. + const [log, updateLog] = useLog(); + + // The contract ID of the deployed contract. + const [contractId, setContractId] = useState(""); + + // An error message to display to the user. + const [drawerOpen, setDrawerOpen] = useState(false); + + // The theme color for the app. + const { themeColor } = useTheme(); + + // Update the ABI in localstorage when the editor changes. + useEffect(() => { + saveAbi(abiCode); + }, [abiCode]); + + const onSwayCodeChange = useCallback( + (code: string) => { + saveSwayCode(code); + setAbiCode(code); + }, + [setAbiCode], + ); + + return ( +
+ +
+ + +
+ + +
+ ); +} + +export default AbiApp; diff --git a/app/src/features/editor/components/AbiEditorView.tsx b/app/src/features/editor/components/AbiEditorView.tsx new file mode 100644 index 0000000..aea7d2a --- /dev/null +++ b/app/src/features/editor/components/AbiEditorView.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import { useIsMobile } from "../../../hooks/useIsMobile"; +import JsonEditor from "./JsonEditor"; +import { TextField } from "@mui/material"; + +export interface AbiEditorViewProps { + abiCode: string; + onAbiCodeChange: (value: string) => void; + contractId: string; + setContractId: (contractId: string) => void; +} + +function AbiEditorView({ + abiCode, + onAbiCodeChange, + contractId, + setContractId, +}: AbiEditorViewProps) { + const isMobile = useIsMobile(); + + return ( +
+ setContractId(e.target.value)} + style={{ width: "100%", marginBottom: "10px" }} + /> +
+ +
+
+ ); +} + +export default AbiEditorView; diff --git a/app/src/features/editor/components/JsonEditor.tsx b/app/src/features/editor/components/JsonEditor.tsx new file mode 100644 index 0000000..4795154 --- /dev/null +++ b/app/src/features/editor/components/JsonEditor.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import AceEditor from "react-ace"; +import "ace-builds/webpack-resolver"; +import "ace-builds/src-noconflict/mode-json"; +import "ace-builds/src-noconflict/theme-chrome"; +import "ace-builds/src-noconflict/theme-tomorrow_night_bright"; +import { StyledBorder } from "../../../components/shared"; +import useTheme from "../../../context/theme"; + +export interface JsonEditorProps { + code: string; + onChange: (value: string) => void; +} + +function JsonEditor({ code, onChange }: JsonEditorProps) { + const { editorTheme, themeColor } = useTheme(); + + return ( + + + + ); +} + +export default JsonEditor; diff --git a/app/src/features/toolbar/components/AbiActionToolbar.tsx b/app/src/features/toolbar/components/AbiActionToolbar.tsx new file mode 100644 index 0000000..a12fbec --- /dev/null +++ b/app/src/features/toolbar/components/AbiActionToolbar.tsx @@ -0,0 +1,59 @@ +import React, { useCallback } from "react"; +import OpenInNew from "@mui/icons-material/OpenInNew"; +import SecondaryButton from "../../../components/SecondaryButton"; +import { useIsMobile } from "../../../hooks/useIsMobile"; +import SwitchThemeButton from "./SwitchThemeButton"; +import { useConnectIfNotAlready } from "../hooks/useConnectIfNotAlready"; +import { useDisconnect } from "@fuels/react"; + +export interface AbiActionToolbarProps { + drawerOpen: boolean; + setDrawerOpen: (open: boolean) => void; +} + +function AbiActionToolbar({ + drawerOpen, + setDrawerOpen, +}: AbiActionToolbarProps) { + const isMobile = useIsMobile(); + const { isConnected, connect } = useConnectIfNotAlready(); + const { disconnect } = useDisconnect(); + + const onDocsClick = useCallback(() => { + window.open("https://docs.fuel.network/docs/sway", "_blank", "noreferrer"); + }, []); + + return ( +
+ setDrawerOpen(!drawerOpen)} + text="INTERACT" + tooltip="Interact with the contract ABI" + /> + } + /> + + {!isMobile && } +
+ ); +} + +export default AbiActionToolbar; diff --git a/app/src/features/toolbar/components/ActionToolbar.tsx b/app/src/features/toolbar/components/ActionToolbar.tsx index 2568904..729b6c0 100644 --- a/app/src/features/toolbar/components/ActionToolbar.tsx +++ b/app/src/features/toolbar/components/ActionToolbar.tsx @@ -14,6 +14,7 @@ import { useIsMobile } from "../../../hooks/useIsMobile"; import SwitchThemeButton from "./SwitchThemeButton"; import { useConnectIfNotAlready } from "../hooks/useConnectIfNotAlready"; import { useDisconnect } from "@fuels/react"; +import { useNavigate } from "react-router-dom"; export interface ActionToolbarProps { deployState: DeployState; @@ -45,6 +46,7 @@ function ActionToolbar({ const isMobile = useIsMobile(); const { isConnected } = useConnectIfNotAlready(); const { disconnect } = useDisconnect(); + const navigate = useNavigate(); const onDocsClick = useCallback(() => { window.open("https://docs.fuel.network/docs/sway", "_blank", "noreferrer"); @@ -99,6 +101,12 @@ function ActionToolbar({ : "Show the Solidity editor to transpile Solidity to Sway" } /> + navigate("/abi")} + text="ABI" + tooltip="Query an already-deployed contract using the ABI" + /> , }, + { + path: "/abi", + element: , + }, ]); const root = ReactDOM.createRoot(