Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add ABI view #102

Merged
merged 3 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions app/src/AbiApp.tsx
Original file line number Diff line number Diff line change
@@ -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<string>(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 (
<div
style={{
padding: "15px",
margin: "0px",
background: themeColor("white4"),
}}
>
<AbiActionToolbar drawerOpen={drawerOpen} setDrawerOpen={setDrawerOpen} />
<div
style={{
marginRight: drawerOpen ? DRAWER_WIDTH : 0,
transition: "margin 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms",
height: "calc(100vh - 95px)",
display: "flex",
flexDirection: "column",
}}
>
<AbiEditorView
abiCode={abiCode}
onAbiCodeChange={onSwayCodeChange}
contractId={contractId}
setContractId={setContractId}
/>
<LogView results={log} />
</div>
<InteractionDrawer
isOpen={drawerOpen}
width={DRAWER_WIDTH}
contractId={contractId}
updateLog={updateLog}
/>
<Analytics />
</div>
);
}

export default AbiApp;
50 changes: 50 additions & 0 deletions app/src/features/editor/components/AbiEditorView.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div>
<TextField
id="contract-id"
label="Contract ID"
variant="outlined"
size="small"
value={contractId}
onChange={(e) => setContractId(e.target.value)}
style={{ width: "100%", marginBottom: "10px" }}
/>
<div
style={{
display: "flex",
flexDirection: isMobile ? "column" : "row",
height: "50vh",
minHeight: "10vh",
maxHeight: "80vh",
position: "relative",
resize: isMobile ? "none" : "vertical",
overflow: "auto",
}}
>
<JsonEditor code={abiCode} onChange={onAbiCodeChange} />
</div>
</div>
);
}

export default AbiEditorView;
37 changes: 37 additions & 0 deletions app/src/features/editor/components/JsonEditor.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<StyledBorder style={{ flex: 1 }} themeColor={themeColor}>
<AceEditor
style={{
width: "100%",
height: "100%",
}}
mode="json"
theme={editorTheme}
name="editor"
fontSize="14px"
onChange={onChange}
value={code}
editorProps={{ $blockScrolling: true }}
/>
</StyledBorder>
);
}

export default JsonEditor;
59 changes: 59 additions & 0 deletions app/src/features/toolbar/components/AbiActionToolbar.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div
style={{
margin: "5px 0 10px",
display: isMobile ? "inline-table" : "flex",
}}
>
<SecondaryButton
header={true}
onClick={() => setDrawerOpen(!drawerOpen)}
text="INTERACT"
tooltip="Interact with the contract ABI"
/>
<SecondaryButton
header={true}
onClick={onDocsClick}
text="DOCS"
tooltip={"Open documentation for Sway in a new tab"}
endIcon={<OpenInNew style={{ fontSize: "16px" }} />}
/>
<SecondaryButton
header={true}
onClick={isConnected ? disconnect : connect}
text={isConnected ? "DISCONNECT" : "CONNECT"}
tooltip={
isConnected ? "Disconnect from the wallet" : "Connect to a wallet"
}
/>
{!isMobile && <SwitchThemeButton />}
</div>
);
}

export default AbiActionToolbar;
8 changes: 8 additions & 0 deletions app/src/features/toolbar/components/ActionToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -99,6 +101,12 @@ function ActionToolbar({
: "Show the Solidity editor to transpile Solidity to Sway"
}
/>
<SecondaryButton
header={true}
onClick={() => navigate("/abi")}
text="ABI"
tooltip="Query an already-deployed contract using the ABI"
/>
<SecondaryButton
header={true}
onClick={onDocsClick}
Expand Down
2 changes: 1 addition & 1 deletion app/src/features/toolbar/hooks/useConnectIfNotAlready.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ export function useConnectIfNotAlready() {
}
}, [isError, failedCallbackRef]);

return { connectIfNotAlready, isConnected };
return { connectIfNotAlready, isConnected, connect };
}
5 changes: 5 additions & 0 deletions app/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import AbiApp from "./AbiApp";
import reportWebVitals from "./reportWebVitals";
import "./index.css";
import { Providers } from "./components/Providers";
Expand All @@ -11,6 +12,10 @@ const router = createBrowserRouter([
path: "/",
element: <App />,
},
{
path: "/abi",
element: <AbiApp />,
},
]);

const root = ReactDOM.createRoot(
Expand Down
Loading