| import React from "react"; | |
| import { useSelector } from "react-redux"; | |
| import { useFiles } from "#/context/files"; | |
| import { cn } from "#/utils/utils"; | |
| import { useListFiles } from "#/hooks/query/use-list-files"; | |
| import { useListFile } from "#/hooks/query/use-list-file"; | |
| import { Filename } from "./filename"; | |
| import { RootState } from "#/store"; | |
| interface TreeNodeProps { | |
| path: string; | |
| defaultOpen?: boolean; | |
| } | |
| function TreeNode({ path, defaultOpen = false }: TreeNodeProps) { | |
| const { setFileContent, setSelectedPath, files, selectedPath } = useFiles(); | |
| const [isOpen, setIsOpen] = React.useState(defaultOpen); | |
| const { curAgentState } = useSelector((state: RootState) => state.agent); | |
| const isDirectory = path.endsWith("/"); | |
| const { data: paths } = useListFiles({ | |
| path, | |
| enabled: isDirectory && isOpen, | |
| }); | |
| const { data: fileContent, refetch } = useListFile({ path }); | |
| React.useEffect(() => { | |
| if (fileContent) { | |
| if (fileContent !== files[path]) { | |
| setFileContent(path, fileContent); | |
| } | |
| } | |
| }, [fileContent, path]); | |
| React.useEffect(() => { | |
| if (selectedPath === path && !isDirectory) { | |
| refetch(); | |
| } | |
| }, [curAgentState, selectedPath, path, isDirectory]); | |
| const fileParts = path.split("/"); | |
| const filename = | |
| fileParts[fileParts.length - 1] || fileParts[fileParts.length - 2]; | |
| const handleClick = async () => { | |
| if (isDirectory) setIsOpen((prev) => !prev); | |
| else { | |
| setSelectedPath(path); | |
| await refetch(); | |
| } | |
| }; | |
| return ( | |
| <div | |
| className={cn( | |
| "text-sm text-neutral-400", | |
| path === selectedPath && "bg-gray-700", | |
| )} | |
| > | |
| <button | |
| type={isDirectory ? "button" : "submit"} | |
| name="file" | |
| value={path} | |
| onClick={handleClick} | |
| className="flex items-center justify-between w-full px-1" | |
| > | |
| <Filename | |
| name={filename} | |
| type={isDirectory ? "folder" : "file"} | |
| isOpen={isOpen} | |
| /> | |
| </button> | |
| {isOpen && paths && ( | |
| <div className="ml-5"> | |
| {paths.map((child, index) => ( | |
| <TreeNode key={index} path={child} /> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |
| export default React.memo(TreeNode); | |