import { useCallback, useEffect, useMemo, useState } from "react"; import { ResizableHandle, ResizablePanel, ResizablePanelGroup, } from "~/components/ui/resizable"; import Tabs, { Tab } from "~/components/ui/tabs"; import FileViewer from "./file-viewer"; import trpc from "~/lib/trpc"; import EditorContext from "../context/editor"; import type { FileSchema } from "~/server/db/schema/file"; import Panel from "~/components/ui/panel"; import { useProjectContext } from "../context/project"; import Sidebar from "./sidebar"; import ConsoleLogger from "./console-logger"; import { useData } from "~/renderer/hooks"; import { Data } from "../+data"; import { useBreakpoint } from "~/hooks/useBreakpoint"; import StatusBar from "./status-bar"; import { FiTerminal } from "react-icons/fi"; import SettingsDialog from "./settings-dialog"; import FileIcon from "~/components/ui/file-icon"; const Editor = () => { const { project, initialFiles } = useData(); const trpcUtils = trpc.useUtils(); const projectCtx = useProjectContext(); const [breakpoint] = useBreakpoint(); const [curTabIdx, setCurTabIdx] = useState(0); const [curOpenFiles, setOpenFiles] = useState( initialFiles.map((i) => i.id) ); const openedFilesData = trpc.file.getAll.useQuery( { projectId: project.id, id: curOpenFiles }, { enabled: curOpenFiles.length > 0, initialData: initialFiles } ); const [openedFiles, setOpenedFiles] = useState(initialFiles); const deleteFile = trpc.file.delete.useMutation({ onSuccess: (file) => { trpcUtils.file.getAll.invalidate(); onFileChanged(file); const openFileIdx = curOpenFiles.indexOf(file.id); if (openFileIdx >= 0) { onCloseFile(openFileIdx); } }, }); useEffect(() => { if (!initialFiles?.length || curOpenFiles.length > 0) { return; } initialFiles.forEach((file) => { onOpenFile(file.id, false); }); return () => { setOpenFiles([]); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [initialFiles]); useEffect(() => { if (openedFilesData.data) { setOpenedFiles(openedFilesData.data); } }, [openedFilesData.data]); const onOpenFile = useCallback( (fileId: number, autoSwitchTab = true) => { const idx = curOpenFiles.indexOf(fileId); if (idx >= 0) { return setCurTabIdx(idx); } setOpenFiles((state) => { if (autoSwitchTab) { setCurTabIdx(state.length); } return [...state, fileId]; }); }, [curOpenFiles] ); const onDeleteFile = useCallback( (fileId: number) => { if ( window.confirm("Are you sure want to delete this files?") && !deleteFile.isPending ) { deleteFile.mutate(fileId); } }, [deleteFile] ); const onCloseFile = useCallback( (idx: number) => { const _f = [...curOpenFiles]; _f.splice(idx, 1); setOpenFiles(_f); if (curTabIdx === idx) { setCurTabIdx(Math.max(0, idx - 1)); } }, [curOpenFiles, curTabIdx] ); const onFileChanged = useCallback( (_file: Omit) => { openedFilesData.refetch(); }, [openedFilesData] ); const tabs = useMemo(() => { let tabs: Tab[] = []; // opened files tabs = tabs.concat( curOpenFiles.map((fileId) => { const fileData = openedFiles?.find((i) => i.id === fileId); const filename = fileData?.filename || "..."; return { title: filename, icon: , render: () => , }; }) ); // show console tab on mobile if (breakpoint < 2) { tabs.push({ title: "Console", icon: , render: () => , locked: true, }); } return tabs; }, [curOpenFiles, openedFiles, breakpoint]); const PanelComponent = !projectCtx.isCompact ? Panel : "div"; return ( {breakpoint >= 2 ? ( <> ) : null} ); }; export default Editor;