From b2bd2fcc5e38a14d4288e5e1307e83e5c544135d Mon Sep 17 00:00:00 2001 From: Poafs1 Date: Mon, 27 Jan 2025 17:14:38 +0700 Subject: [PATCH 1/6] feat(components): add contract code preview --- pnpm-lock.yaml | 10 +- src/lib/components/editor/Editor.tsx | 26 +++++ src/lib/components/editor/EditorFileBody.tsx | 45 ++++++++ src/lib/components/editor/EditorSidebar.tsx | 94 ++++++++++++++++ src/lib/components/editor/EditorTop.tsx | 105 ++++++++++++++++++ src/lib/components/editor/FullEditor.tsx | 79 +++++++++++++ src/lib/components/editor/helpers.ts | 35 ++++++ .../{module => editor}/moveSyntax.ts | 0 src/lib/components/editor/types.ts | 14 +++ src/lib/components/icon/SvgIcon.tsx | 31 ++++++ .../components/module/ModuleSourceCode.tsx | 22 +--- .../ContractAbi.tsx | 0 .../ContractByteCode.tsx | 0 .../ContractCode.tsx | 36 ++++++ .../ContractCompiler.tsx | 0 .../index.tsx | 7 ++ src/lib/pages/evm-contract-details/index.tsx | 2 +- src/lib/services/types/verification/evm.ts | 1 + src/lib/styles/globals.css | 5 + 19 files changed, 486 insertions(+), 26 deletions(-) create mode 100644 src/lib/components/editor/Editor.tsx create mode 100644 src/lib/components/editor/EditorFileBody.tsx create mode 100644 src/lib/components/editor/EditorSidebar.tsx create mode 100644 src/lib/components/editor/EditorTop.tsx create mode 100644 src/lib/components/editor/FullEditor.tsx create mode 100644 src/lib/components/editor/helpers.ts rename src/lib/components/{module => editor}/moveSyntax.ts (100%) create mode 100644 src/lib/components/editor/types.ts rename src/lib/pages/evm-contract-details/components/{EvmContractDetailsContarct => EvmContractDetailsContract}/ContractAbi.tsx (100%) rename src/lib/pages/evm-contract-details/components/{EvmContractDetailsContarct => EvmContractDetailsContract}/ContractByteCode.tsx (100%) create mode 100644 src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCode.tsx rename src/lib/pages/evm-contract-details/components/{EvmContractDetailsContarct => EvmContractDetailsContract}/ContractCompiler.tsx (100%) rename src/lib/pages/evm-contract-details/components/{EvmContractDetailsContarct => EvmContractDetailsContract}/index.tsx (88%) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6384af363..66395bf9c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -163,7 +163,7 @@ importers: version: 1.1.0(next@14.2.23(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(svelte@4.2.19) ace-builds: specifier: ^1.12.5 - version: 1.37.3 + version: 1.37.5 axios: specifier: ^1.7.4 version: 1.7.9 @@ -4763,8 +4763,8 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} - ace-builds@1.37.3: - resolution: {integrity: sha512-LXMNR57LGyUaJZoAXqVuy6x/ipQvj62iKUHv0DlOb57DNRiV3ZIvYNTVeBHCkUeQAc3BBmyLWC2uvMixSbhj9Q==} + ace-builds@1.37.5: + resolution: {integrity: sha512-VMJ4Cnhq6L9dwvOCyuyyvQuiVTSwdZC7zDKJBBBJJax0wGQ7MvzQZFoi0gMmCm2I4Zuv/ZbtwU/dlglIhCNLhw==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -17501,7 +17501,7 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 - ace-builds@1.37.3: {} + ace-builds@1.37.5: {} acorn-jsx@5.3.2(acorn@7.4.1): dependencies: @@ -22549,7 +22549,7 @@ snapshots: react-ace@10.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - ace-builds: 1.37.3 + ace-builds: 1.37.5 diff-match-patch: 1.0.5 lodash.get: 4.4.2 lodash.isequal: 4.5.0 diff --git a/src/lib/components/editor/Editor.tsx b/src/lib/components/editor/Editor.tsx new file mode 100644 index 000000000..05c34b2a1 --- /dev/null +++ b/src/lib/components/editor/Editor.tsx @@ -0,0 +1,26 @@ +import type { EditorProps, Monaco } from "@monaco-editor/react"; +import MonacoEditor from "@monaco-editor/react"; + +import { moveLanguageConfig, moveTokenProvider } from "./moveSyntax"; + +const loadMoveSyntax = (monaco: Monaco) => { + monaco.languages.register({ id: "move" }); + monaco.languages.onLanguage("move", () => { + monaco.languages.setMonarchTokensProvider("move", moveTokenProvider); + monaco.languages.setLanguageConfiguration("move", moveLanguageConfig); + }); +}; + +export const Editor = ({ ...props }: EditorProps) => ( + +); diff --git a/src/lib/components/editor/EditorFileBody.tsx b/src/lib/components/editor/EditorFileBody.tsx new file mode 100644 index 000000000..330e58775 --- /dev/null +++ b/src/lib/components/editor/EditorFileBody.tsx @@ -0,0 +1,45 @@ +import { Flex, FlexProps, Text } from "@chakra-ui/react"; +import { SourceTreeNode } from "./types"; +import { CustomIcon } from "../icon"; + +interface EditorFileBodyProps extends FlexProps { + node: SourceTreeNode; + initialFilePath: string; + isNoWrap?: boolean; +} + +export const EditorFileBody = ({ + node, + initialFilePath, + isNoWrap, + ...props +}: EditorFileBodyProps) => ( + + {node.isFolder ? ( + <> + + + + ) : ( + <> + {initialFilePath === node.path && ( + + )} + + + )} + + {node.name} + + +); diff --git a/src/lib/components/editor/EditorSidebar.tsx b/src/lib/components/editor/EditorSidebar.tsx new file mode 100644 index 000000000..290433f64 --- /dev/null +++ b/src/lib/components/editor/EditorSidebar.tsx @@ -0,0 +1,94 @@ +import { useState } from "react"; +import { SourceTreeNode } from "./types"; +import { Box, Flex } from "@chakra-ui/react"; +import { EditorFileBody } from "./EditorFileBody"; +import { Nullable } from "lib/types"; + +interface EditorSidebarProps { + sourceTreeNode: SourceTreeNode[]; + selectedFile: Nullable; + initialFilePath: string; + onClick: (node: SourceTreeNode) => void; +} + +const EditorSidebarSelectedMark = () => ( + + + +); + +export const EditorSidebar = ({ + sourceTreeNode, + initialFilePath, + onClick, + selectedFile, +}: EditorSidebarProps) => { + const [tree, setTree] = useState(sourceTreeNode); + + const handleUpdateIsOpen = ( + node: SourceTreeNode, + tree: SourceTreeNode[] + ): SourceTreeNode[] => + tree.map((n) => { + if (n.path === node.path) { + return { ...n, isOpen: !n.isOpen }; + } + + return { ...n, children: handleUpdateIsOpen(node, n.children) }; + }); + + const onUpdateIsOpen = (node: SourceTreeNode) => { + const updatedTree = handleUpdateIsOpen(node, tree); + setTree(updatedTree); + }; + + return tree.map((node) => { + const isSelected = node.path === selectedFile?.path; + + return ( + + + (node.isFolder ? onUpdateIsOpen(node) : onClick(node))} + > + {isSelected && } + + + {node.children.length > 0 && node.isOpen && ( + + )} + + ); + }); +}; diff --git a/src/lib/components/editor/EditorTop.tsx b/src/lib/components/editor/EditorTop.tsx new file mode 100644 index 000000000..8eb51334a --- /dev/null +++ b/src/lib/components/editor/EditorTop.tsx @@ -0,0 +1,105 @@ +import { Box, Flex, Text } from "@chakra-ui/react"; +import { SourceTreeNode } from "./types"; +import { EditorFileBody } from "./EditorFileBody"; +import { CustomIcon } from "../icon"; +import { Nullable } from "lib/types"; + +interface EditorTopProps { + filesList: SourceTreeNode[]; + selectedFile: Nullable; + initialFilePath: string; + onClick: (index: number) => void; + onRemove: (node: SourceTreeNode, index: number) => void; +} + +const vsCodeDarkColor = "#1E1E1E"; + +export const EditorTop = ({ + filesList, + selectedFile, + initialFilePath, + onClick, + onRemove, +}: EditorTopProps) => ( + + + {filesList.map((node, index) => { + const isInitialFilePath = initialFilePath === node.path; + const isSelected = selectedFile?.path === node.path; + + return ( + onClick(index)} + sx={{ + borderWidth: "1px", + borderTopColor: isSelected ? "primary.main" : vsCodeDarkColor, + borderLeftColor: "transparent", + borderRightColor: "gray.700", + borderBottomColor: isSelected ? vsCodeDarkColor : "gray.700", + bgColor: isSelected ? vsCodeDarkColor : "gray.900", + "&:first-child": { + borderLeftColor: vsCodeDarkColor, + }, + }} + > + + {!isInitialFilePath && ( + { + e.stopPropagation(); + onRemove(node, index); + }} + /> + )} + + ); + })} + + {selectedFile && ( + + + {selectedFile.path.split("/").map((path, index) => ( + <> + {index !== 0 && ( + + )} + + {path} + + + ))} + + + )} + +); diff --git a/src/lib/components/editor/FullEditor.tsx b/src/lib/components/editor/FullEditor.tsx new file mode 100644 index 000000000..c8085ba5f --- /dev/null +++ b/src/lib/components/editor/FullEditor.tsx @@ -0,0 +1,79 @@ +import { Box, Grid, Stack, Text } from "@chakra-ui/react"; +import { generateSourceTree } from "./helpers"; +import { FilePath, SourceTreeNode } from "./types"; +import { EditorSidebar } from "./EditorSidebar"; +import { useEffect, useState } from "react"; +import { EditorTop } from "./EditorTop"; +import { Nullable } from "lib/types"; +import { Editor } from "./Editor"; + +interface FullEditorProps { + filesPath: FilePath[]; + initialFilePath: string; +} + +export const FullEditor = ({ filesPath, initialFilePath }: FullEditorProps) => { + const [filesList, setFilesList] = useState([]); + const [selectedFile, setSelectedFile] = + useState>(null); + const generatedSourceTree = generateSourceTree(filesPath); + + const handleFindInitialFile = (tree: SourceTreeNode[]) => { + return tree.forEach((node) => { + if (node.path === initialFilePath) { + setFilesList([node]); + setSelectedFile(node); + return node; + } else if (node.children.length > 0) { + handleFindInitialFile(node.children); + } + + return undefined; + }); + }; + + useEffect(() => { + handleFindInitialFile(generatedSourceTree); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [setFilesList]); + + const handleUpdateFilesList = (node: SourceTreeNode) => { + setSelectedFile(node); + const foundNode = filesList.find((n) => n.path === node.path); + if (foundNode) return; + setFilesList([...filesList, node]); + }; + + const handleOnRemove = (node: SourceTreeNode, index: number) => { + const filteredFiles = filesList.filter((_, i) => i !== index); + setFilesList(filteredFiles); + if (selectedFile?.path === node.path) + setSelectedFile(filteredFiles[filteredFiles.length - 1]); + }; + + return ( + + + Files + + + + + + setSelectedFile(filesList[index])} + onRemove={handleOnRemove} + filesList={filesList} + initialFilePath={initialFilePath} + /> + + + + ); +}; diff --git a/src/lib/components/editor/helpers.ts b/src/lib/components/editor/helpers.ts new file mode 100644 index 000000000..72c68d647 --- /dev/null +++ b/src/lib/components/editor/helpers.ts @@ -0,0 +1,35 @@ +import { last, split } from "lodash"; +import { EXTENSION_LIB, FilePath, SourceTreeNode } from "./types"; + +export const generateSourceTree = (filesPath: FilePath[]): SourceTreeNode[] => { + const root: SourceTreeNode[] = []; + + filesPath.forEach(({ path, code }) => { + const parts = path.split("/"); + let currentLevel = root; + + parts.forEach((part, index) => { + let existingNode = currentLevel.find((node) => node.name === part); + + if (!existingNode) { + const extension = last(split(part, ".")); + const isFolder = extension ? !EXTENSION_LIB.includes(extension) : false; + + existingNode = { + name: part, + isOpen: index === 0 ? true : false, + children: [], + isFolder, + treeLevel: index, + code, + path: parts.slice(0, index + 1).join("/"), + }; + currentLevel.push(existingNode); + } + + currentLevel = existingNode.children; + }); + }); + + return root; +}; diff --git a/src/lib/components/module/moveSyntax.ts b/src/lib/components/editor/moveSyntax.ts similarity index 100% rename from src/lib/components/module/moveSyntax.ts rename to src/lib/components/editor/moveSyntax.ts diff --git a/src/lib/components/editor/types.ts b/src/lib/components/editor/types.ts new file mode 100644 index 000000000..d02559d10 --- /dev/null +++ b/src/lib/components/editor/types.ts @@ -0,0 +1,14 @@ +export const EXTENSION_LIB = ["sol"]; + +export interface FilePath { + path: string; + code: string; +} + +export interface SourceTreeNode extends FilePath { + name: string; + isOpen: boolean; + isFolder: boolean; + treeLevel: number; + children: SourceTreeNode[]; +} diff --git a/src/lib/components/icon/SvgIcon.tsx b/src/lib/components/icon/SvgIcon.tsx index bb4db85b6..5d817f59e 100644 --- a/src/lib/components/icon/SvgIcon.tsx +++ b/src/lib/components/icon/SvgIcon.tsx @@ -351,6 +351,37 @@ export const ICONS = { ), viewBox: "0 -3.5 16 16", }, + "code-file": { + svg: ( + <> + + + + + + ), + viewBox: "0 0 16 17", + }, collection: { svg: ( <> diff --git a/src/lib/components/module/ModuleSourceCode.tsx b/src/lib/components/module/ModuleSourceCode.tsx index 0a2281545..acc7788aa 100644 --- a/src/lib/components/module/ModuleSourceCode.tsx +++ b/src/lib/components/module/ModuleSourceCode.tsx @@ -11,8 +11,6 @@ import { Heading, Text, } from "@chakra-ui/react"; -import type { Monaco } from "@monaco-editor/react"; -import MonacoEditor from "@monaco-editor/react"; import { AppLink } from "../AppLink"; import { CopyButton } from "../copy"; @@ -22,16 +20,7 @@ import type { MoveVerifyInfoResponse } from "lib/services/types"; import { MoveVerifyStatus } from "lib/types"; import type { Nullish } from "lib/types"; import { formatUTC } from "lib/utils"; - -import { moveLanguageConfig, moveTokenProvider } from "./moveSyntax"; - -const loadMoveSyntax = (monaco: Monaco) => { - monaco.languages.register({ id: "move" }); - monaco.languages.onLanguage("move", () => { - monaco.languages.setMonarchTokensProvider("move", moveTokenProvider); - monaco.languages.setLanguageConfiguration("move", moveLanguageConfig); - }); -}; +import { Editor } from "../editor/Editor"; interface ModuleSourceCodeProps { verificationData: Nullish; @@ -120,14 +109,7 @@ export const ModuleSourceCode = ({ borderColor="gray.700" borderRadius="8px" > - + diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContarct/ContractAbi.tsx b/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractAbi.tsx similarity index 100% rename from src/lib/pages/evm-contract-details/components/EvmContractDetailsContarct/ContractAbi.tsx rename to src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractAbi.tsx diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContarct/ContractByteCode.tsx b/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractByteCode.tsx similarity index 100% rename from src/lib/pages/evm-contract-details/components/EvmContractDetailsContarct/ContractByteCode.tsx rename to src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractByteCode.tsx diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCode.tsx b/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCode.tsx new file mode 100644 index 000000000..70373c6b6 --- /dev/null +++ b/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCode.tsx @@ -0,0 +1,36 @@ +import { Badge, Flex, Heading, Stack } from "@chakra-ui/react"; +import { FullEditor } from "lib/components/editor/FullEditor"; +import { EvmVerifyInfoSourceFile } from "lib/services/types"; + +interface ContractCodeProps { + sourceFiles: EvmVerifyInfoSourceFile[]; + contractPath: string; +} + +export const ContractCode = ({ + sourceFiles, + contractPath, +}: ContractCodeProps) => ( + + + + + Contract source code + + {sourceFiles.length} + + ({ + path: file.sourcePath, + code: file.evmSourceFile.content, + }))} + initialFilePath={contractPath} + /> + + {/* + + Constructor Arguments + + */} + +); diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContarct/ContractCompiler.tsx b/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCompiler.tsx similarity index 100% rename from src/lib/pages/evm-contract-details/components/EvmContractDetailsContarct/ContractCompiler.tsx rename to src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCompiler.tsx diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContarct/index.tsx b/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/index.tsx similarity index 88% rename from src/lib/pages/evm-contract-details/components/EvmContractDetailsContarct/index.tsx rename to src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/index.tsx index 7a944c80c..77325299e 100644 --- a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContarct/index.tsx +++ b/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/index.tsx @@ -9,6 +9,7 @@ import { ContractAbi } from "./ContractAbi"; import { useEvmVerifyInfo } from "lib/services/verification/evm"; import { Loading } from "lib/components/Loading"; import { ContractCompiler } from "./ContractCompiler"; +import { ContractCode } from "./ContractCode"; interface EvmContractDetailsContractProps extends ContractByteCodeProps { contractAddress: HexAddr20; @@ -45,6 +46,12 @@ export const EvmContractDetailsContract = ({ currentTab={currentTab} /> + {currentTab === EvmContractDetailsContractTabs.Code && ( + + )} {currentTab === EvmContractDetailsContractTabs.Compiler && ( )} diff --git a/src/lib/pages/evm-contract-details/index.tsx b/src/lib/pages/evm-contract-details/index.tsx index 61d219ddc..a4a62ac62 100644 --- a/src/lib/pages/evm-contract-details/index.tsx +++ b/src/lib/pages/evm-contract-details/index.tsx @@ -24,7 +24,7 @@ import { useEvmTxHashByCosmosTxHash } from "lib/services/tx"; import type { HexAddr20 } from "lib/types"; import { isHexWalletAddress, truncate } from "lib/utils"; -import { EvmContractDetailsContract } from "./components/EvmContractDetailsContarct"; +import { EvmContractDetailsContract } from "./components/EvmContractDetailsContract"; import { EvmContractDetailsOverview } from "./components/EvmContractDetailsOverview"; import { EvmContractDetailsTop } from "./components/EvmContractDetailsTop"; import { EvmContractDetailsTxs } from "./components/EvmContractDetailsTxs"; diff --git a/src/lib/services/types/verification/evm.ts b/src/lib/services/types/verification/evm.ts index 50d645ed2..db8bdcec6 100644 --- a/src/lib/services/types/verification/evm.ts +++ b/src/lib/services/types/verification/evm.ts @@ -44,6 +44,7 @@ const zEvmVerifyInfoSourceFile = z }), }) .transform(snakeToCamel); +export type EvmVerifyInfoSourceFile = z.infer; export const zEvmVerifyInfo = z .object({ diff --git a/src/lib/styles/globals.css b/src/lib/styles/globals.css index d0f97a9ce..20023d047 100644 --- a/src/lib/styles/globals.css +++ b/src/lib/styles/globals.css @@ -27,3 +27,8 @@ div[data-floating-ui-portal] { visibility: hidden; background-color: unset; } + +/* For update on window shrink down */ +.monaco-editor { + position: absolute !important; +} From 6ec854687405bfbac4eb6148511738f8772fe614 Mon Sep 17 00:00:00 2001 From: Poafs1 Date: Tue, 28 Jan 2025 11:09:13 +0700 Subject: [PATCH 2/6] feat(components): add full editor preview --- src/lib/components/editor/Editor.tsx | 1 + src/lib/components/editor/EditorSidebar.tsx | 2 +- src/lib/components/editor/FullEditor.tsx | 59 +++++++++++---- .../editor/FullEditorSidebarMobile.tsx | 39 ++++++++++ src/lib/components/editor/helpers.ts | 9 ++- .../ContractCode.tsx | 74 +++++++++++++------ .../EvmContractDetailsContract/index.tsx | 1 + src/lib/pages/interact/index.tsx | 2 +- 8 files changed, 146 insertions(+), 41 deletions(-) create mode 100644 src/lib/components/editor/FullEditorSidebarMobile.tsx diff --git a/src/lib/components/editor/Editor.tsx b/src/lib/components/editor/Editor.tsx index 05c34b2a1..858f04cb5 100644 --- a/src/lib/components/editor/Editor.tsx +++ b/src/lib/components/editor/Editor.tsx @@ -20,6 +20,7 @@ export const Editor = ({ ...props }: EditorProps) => ( options={{ readOnly: true, scrollBeyondLastLine: false, + automaticLayout: true, }} {...props} /> diff --git a/src/lib/components/editor/EditorSidebar.tsx b/src/lib/components/editor/EditorSidebar.tsx index 290433f64..beab290e1 100644 --- a/src/lib/components/editor/EditorSidebar.tsx +++ b/src/lib/components/editor/EditorSidebar.tsx @@ -4,7 +4,7 @@ import { Box, Flex } from "@chakra-ui/react"; import { EditorFileBody } from "./EditorFileBody"; import { Nullable } from "lib/types"; -interface EditorSidebarProps { +export interface EditorSidebarProps { sourceTreeNode: SourceTreeNode[]; selectedFile: Nullable; initialFilePath: string; diff --git a/src/lib/components/editor/FullEditor.tsx b/src/lib/components/editor/FullEditor.tsx index c8085ba5f..f0c09857f 100644 --- a/src/lib/components/editor/FullEditor.tsx +++ b/src/lib/components/editor/FullEditor.tsx @@ -6,17 +6,29 @@ import { useEffect, useState } from "react"; import { EditorTop } from "./EditorTop"; import { Nullable } from "lib/types"; import { Editor } from "./Editor"; +import { useMobile } from "lib/app-provider"; +import { + FullEditorSidebarMobile, + FullEditorSidebarMobileProps, +} from "./FullEditorSidebarMobile"; -interface FullEditorProps { +interface FullEditorProps + extends Pick { filesPath: FilePath[]; initialFilePath: string; } -export const FullEditor = ({ filesPath, initialFilePath }: FullEditorProps) => { +export const FullEditor = ({ + filesPath, + initialFilePath, + isOpen, + onClose, +}: FullEditorProps) => { + const isMobile = useMobile(); const [filesList, setFilesList] = useState([]); const [selectedFile, setSelectedFile] = useState>(null); - const generatedSourceTree = generateSourceTree(filesPath); + const generatedSourceTree = generateSourceTree(filesPath, initialFilePath); const handleFindInitialFile = (tree: SourceTreeNode[]) => { return tree.forEach((node) => { @@ -39,6 +51,7 @@ export const FullEditor = ({ filesPath, initialFilePath }: FullEditorProps) => { const handleUpdateFilesList = (node: SourceTreeNode) => { setSelectedFile(node); + onClose(); const foundNode = filesList.find((n) => n.path === node.path); if (foundNode) return; setFilesList([...filesList, node]); @@ -52,18 +65,34 @@ export const FullEditor = ({ filesPath, initialFilePath }: FullEditorProps) => { }; return ( - - - Files - - - - + + {isMobile ? ( + + ) : ( + + Files + + + + + )} void; +} + +export const FullEditorSidebarMobile = ({ + isOpen, + onClose, + ...props +}: FullEditorSidebarMobileProps) => ( + + + + + + File Tree + + + + + + + + + + +); diff --git a/src/lib/components/editor/helpers.ts b/src/lib/components/editor/helpers.ts index 72c68d647..0350c3e9c 100644 --- a/src/lib/components/editor/helpers.ts +++ b/src/lib/components/editor/helpers.ts @@ -1,7 +1,10 @@ import { last, split } from "lodash"; import { EXTENSION_LIB, FilePath, SourceTreeNode } from "./types"; -export const generateSourceTree = (filesPath: FilePath[]): SourceTreeNode[] => { +export const generateSourceTree = ( + filesPath: FilePath[], + initialFilePath: string +): SourceTreeNode[] => { const root: SourceTreeNode[] = []; filesPath.forEach(({ path, code }) => { @@ -14,10 +17,12 @@ export const generateSourceTree = (filesPath: FilePath[]): SourceTreeNode[] => { if (!existingNode) { const extension = last(split(part, ".")); const isFolder = extension ? !EXTENSION_LIB.includes(extension) : false; + const isInitializeNodePath = initialFilePath === path; + const isOpen = index === 0 ? true : false || isInitializeNodePath; existingNode = { name: part, - isOpen: index === 0 ? true : false, + isOpen, children: [], isFolder, treeLevel: index, diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCode.tsx b/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCode.tsx index 70373c6b6..515576f0e 100644 --- a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCode.tsx +++ b/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCode.tsx @@ -1,36 +1,66 @@ -import { Badge, Flex, Heading, Stack } from "@chakra-ui/react"; +import { + Badge, + Button, + Flex, + Heading, + Stack, + useDisclosure, +} from "@chakra-ui/react"; import { FullEditor } from "lib/components/editor/FullEditor"; +import { TextReadOnly } from "lib/components/json/TextReadOnly"; import { EvmVerifyInfoSourceFile } from "lib/services/types"; interface ContractCodeProps { sourceFiles: EvmVerifyInfoSourceFile[]; contractPath: string; + constructorArguments: string; } export const ContractCode = ({ sourceFiles, contractPath, -}: ContractCodeProps) => ( - - - + constructorArguments, +}: ContractCodeProps) => { + const { isOpen, onClose, onOpen } = useDisclosure(); + + return ( + + + + + + Contract source code + + {sourceFiles.length} + + + + ({ + path: file.sourcePath, + code: file.evmSourceFile.content, + }))} + initialFilePath={contractPath} + isOpen={isOpen} + onClose={onClose} + /> + + - Contract source code + Constructor Arguments - {sourceFiles.length} - - ({ - path: file.sourcePath, - code: file.evmSourceFile.content, - }))} - initialFilePath={contractPath} - /> + + - {/* - - Constructor Arguments - - */} - -); + ); +}; diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/index.tsx b/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/index.tsx index 77325299e..4011e96c8 100644 --- a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/index.tsx +++ b/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/index.tsx @@ -50,6 +50,7 @@ export const EvmContractDetailsContract = ({ )} {currentTab === EvmContractDetailsContractTabs.Compiler && ( diff --git a/src/lib/pages/interact/index.tsx b/src/lib/pages/interact/index.tsx index a091b6467..05aac4800 100644 --- a/src/lib/pages/interact/index.tsx +++ b/src/lib/pages/interact/index.tsx @@ -155,7 +155,7 @@ const InteractBody = ({ (selectedModuleInput: IndexedModule, fn?: ExposedFunction) => { setModule(selectedModuleInput); setSelectedFn(fn); - handleSetSelectedType(fn?.is_view ?? true ? "view" : "execute"); + handleSetSelectedType((fn?.is_view ?? true) ? "view" : "execute"); navigate({ pathname: "/interact", From e05910b7cfa627f9dd3b3c56f906019f24079146 Mon Sep 17 00:00:00 2001 From: Poafs1 Date: Tue, 28 Jan 2025 11:15:08 +0700 Subject: [PATCH 3/6] fix(components): editor top --- src/lib/components/editor/EditorTop.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/components/editor/EditorTop.tsx b/src/lib/components/editor/EditorTop.tsx index 8eb51334a..f0f331fc8 100644 --- a/src/lib/components/editor/EditorTop.tsx +++ b/src/lib/components/editor/EditorTop.tsx @@ -3,6 +3,7 @@ import { SourceTreeNode } from "./types"; import { EditorFileBody } from "./EditorFileBody"; import { CustomIcon } from "../icon"; import { Nullable } from "lib/types"; +import { Fragment } from "react"; interface EditorTopProps { filesList: SourceTreeNode[]; @@ -52,7 +53,7 @@ export const EditorTop = ({ borderRightColor: "gray.700", borderBottomColor: isSelected ? vsCodeDarkColor : "gray.700", bgColor: isSelected ? vsCodeDarkColor : "gray.900", - "&:first-child": { + "&:first-of-type": { borderLeftColor: vsCodeDarkColor, }, }} @@ -89,14 +90,14 @@ export const EditorTop = ({ borderTopColor="gray.700" > {selectedFile.path.split("/").map((path, index) => ( - <> + {index !== 0 && ( )} {path} - + ))} From 49be686012a9508470414d8b0b653a5ee99cef22 Mon Sep 17 00:00:00 2001 From: Poafs1 Date: Tue, 28 Jan 2025 11:20:19 +0700 Subject: [PATCH 4/6] docs: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10c961005..599b30578 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- [#1210](https://github.com/alleslabs/celatone-frontend/pull/1210) Add EVM contract details code preview - [#1207](https://github.com/alleslabs/celatone-frontend/pull/1207) Add EVM contract details compiler settings - [#1206](https://github.com/alleslabs/celatone-frontend/pull/1206) Add EVM contract details abi - [#1204](https://github.com/alleslabs/celatone-frontend/pull/1204) Add EVM contract details deployed bytecode From f99725ced02bf0f197d5de96fe08a7b603ded1cc Mon Sep 17 00:00:00 2001 From: evilpeach Date: Wed, 29 Jan 2025 14:32:37 +0700 Subject: [PATCH 5/6] chore: change folder name --- .../ContractAbi.tsx | 0 .../ContractByteCode.tsx | 0 .../ContractCode.tsx | 0 .../ContractCompiler.tsx | 0 .../index.tsx | 0 src/lib/pages/evm-contract-details/index.tsx | 2 +- 6 files changed, 1 insertion(+), 1 deletion(-) rename src/lib/pages/evm-contract-details/components/{EvmContractDetailsContract => evm-contract-details-contract}/ContractAbi.tsx (100%) rename src/lib/pages/evm-contract-details/components/{EvmContractDetailsContract => evm-contract-details-contract}/ContractByteCode.tsx (100%) rename src/lib/pages/evm-contract-details/components/{EvmContractDetailsContract => evm-contract-details-contract}/ContractCode.tsx (100%) rename src/lib/pages/evm-contract-details/components/{EvmContractDetailsContract => evm-contract-details-contract}/ContractCompiler.tsx (100%) rename src/lib/pages/evm-contract-details/components/{EvmContractDetailsContract => evm-contract-details-contract}/index.tsx (100%) diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractAbi.tsx b/src/lib/pages/evm-contract-details/components/evm-contract-details-contract/ContractAbi.tsx similarity index 100% rename from src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractAbi.tsx rename to src/lib/pages/evm-contract-details/components/evm-contract-details-contract/ContractAbi.tsx diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractByteCode.tsx b/src/lib/pages/evm-contract-details/components/evm-contract-details-contract/ContractByteCode.tsx similarity index 100% rename from src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractByteCode.tsx rename to src/lib/pages/evm-contract-details/components/evm-contract-details-contract/ContractByteCode.tsx diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCode.tsx b/src/lib/pages/evm-contract-details/components/evm-contract-details-contract/ContractCode.tsx similarity index 100% rename from src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCode.tsx rename to src/lib/pages/evm-contract-details/components/evm-contract-details-contract/ContractCode.tsx diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCompiler.tsx b/src/lib/pages/evm-contract-details/components/evm-contract-details-contract/ContractCompiler.tsx similarity index 100% rename from src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/ContractCompiler.tsx rename to src/lib/pages/evm-contract-details/components/evm-contract-details-contract/ContractCompiler.tsx diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/index.tsx b/src/lib/pages/evm-contract-details/components/evm-contract-details-contract/index.tsx similarity index 100% rename from src/lib/pages/evm-contract-details/components/EvmContractDetailsContract/index.tsx rename to src/lib/pages/evm-contract-details/components/evm-contract-details-contract/index.tsx diff --git a/src/lib/pages/evm-contract-details/index.tsx b/src/lib/pages/evm-contract-details/index.tsx index a4a62ac62..7b030fb58 100644 --- a/src/lib/pages/evm-contract-details/index.tsx +++ b/src/lib/pages/evm-contract-details/index.tsx @@ -24,7 +24,7 @@ import { useEvmTxHashByCosmosTxHash } from "lib/services/tx"; import type { HexAddr20 } from "lib/types"; import { isHexWalletAddress, truncate } from "lib/utils"; -import { EvmContractDetailsContract } from "./components/EvmContractDetailsContract"; +import { EvmContractDetailsContract } from "./components/evm-contract-details-contract"; import { EvmContractDetailsOverview } from "./components/EvmContractDetailsOverview"; import { EvmContractDetailsTop } from "./components/EvmContractDetailsTop"; import { EvmContractDetailsTxs } from "./components/EvmContractDetailsTxs"; From 81914ec39d46279c5a6acdc412e1be122df84ff0 Mon Sep 17 00:00:00 2001 From: evilpeach Date: Wed, 29 Jan 2025 15:57:24 +0700 Subject: [PATCH 6/6] fix: handle bytecode undefined --- .../ContractByteCode.tsx | 12 +++++++++--- src/lib/pages/evm-contract-details/index.tsx | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/lib/pages/evm-contract-details/components/evm-contract-details-contract/ContractByteCode.tsx b/src/lib/pages/evm-contract-details/components/evm-contract-details-contract/ContractByteCode.tsx index 53cbe55f9..cfef46bd8 100644 --- a/src/lib/pages/evm-contract-details/components/evm-contract-details-contract/ContractByteCode.tsx +++ b/src/lib/pages/evm-contract-details/components/evm-contract-details-contract/ContractByteCode.tsx @@ -1,4 +1,4 @@ -import { Heading, Stack } from "@chakra-ui/react"; +import { Heading, Stack, Text } from "@chakra-ui/react"; import { TextReadOnly } from "lib/components/json/TextReadOnly"; import { Option } from "lib/types"; @@ -8,7 +8,7 @@ export interface ContractByteCodeProps { } export const ContractByteCode = ({ - byteCode = "", + byteCode, deployedByteCode, }: ContractByteCodeProps) => ( @@ -16,7 +16,13 @@ export const ContractByteCode = ({ ByteCode - + {byteCode ? ( + + ) : ( + + - + + )} diff --git a/src/lib/pages/evm-contract-details/index.tsx b/src/lib/pages/evm-contract-details/index.tsx index 8f63657de..01dc85961 100644 --- a/src/lib/pages/evm-contract-details/index.tsx +++ b/src/lib/pages/evm-contract-details/index.tsx @@ -176,8 +176,8 @@ const EvmContractDetailsBody = ({