From 0a21cb2637c17510379de58e0a7b1478ab539a44 Mon Sep 17 00:00:00 2001 From: Akhilesh Rangani Date: Mon, 15 Jul 2024 14:56:37 -0400 Subject: [PATCH 1/4] fix: store rooms in map --- frontend/components/editor/index.tsx | 96 ++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 26 deletions(-) diff --git a/frontend/components/editor/index.tsx b/frontend/components/editor/index.tsx index 05699f1..45d4962 100644 --- a/frontend/components/editor/index.tsx +++ b/frontend/components/editor/index.tsx @@ -104,6 +104,16 @@ export default function CodeEditor({ const room = useRoom() const [provider, setProvider] = useState() const userInfo = useSelf((me) => me.info) + + // Liveblocks providers map to prevent reinitializing providers + type ProviderData = { + provider: LiveblocksProvider; + yDoc: Y.Doc; + yText: Y.Text; + binding?: MonacoBinding; + onSync: (isSynced: boolean) => void; + }; + const providersMap = useRef(new Map()); // Refs for libraries / features const editorContainerRef = useRef(null) @@ -332,43 +342,77 @@ export default function CodeEditor({ if (!editorRef || !tab || !model) return - const yDoc = new Y.Doc() - const yText = yDoc.getText(tab.id) - const yProvider: any = new LiveblocksProvider(room, yDoc) + let providerData: ProviderData; + + // When a file is opened for the first time, create a new provider and store in providersMap. + if (!providersMap.current.has(tab.id)) { + const yDoc = new Y.Doc(); + const yText = yDoc.getText(tab.id); + const yProvider = new LiveblocksProvider(room, yDoc); - const onSync = (isSynced: boolean) => { - if (isSynced) { - const text = yText.toString() - if (text === "") { - if (activeFileContent) { - yText.insert(0, activeFileContent) - } else { - setTimeout(() => { - yText.insert(0, editorRef.getValue()) - }, 0) + // Inserts the file content into the editor once when the tab is changed. + const onSync = (isSynced: boolean) => { + if (isSynced) { + const text = yText.toString() + if (text === "") { + if (activeFileContent) { + yText.insert(0, activeFileContent) + } else { + setTimeout(() => { + yText.insert(0, editorRef.getValue()) + }, 0) + } } } } + + yProvider.on("sync", onSync) + + // Save the provider to the map. + providerData = { provider: yProvider, yDoc, yText, onSync }; + providersMap.current.set(tab.id, providerData); + + } else { + // When a tab is opened that has been open before, reuse the existing provider. + providerData = providersMap.current.get(tab.id)!; } - yProvider.on("sync", onSync) - - setProvider(yProvider) - const binding = new MonacoBinding( - yText, + providerData.yText, model, new Set([editorRef]), - yProvider.awareness as Awareness - ) + providerData.provider.awareness as unknown as Awareness + ); + + providerData.binding = binding; + + setProvider(providerData.provider); return () => { - yDoc.destroy() - yProvider.destroy() - binding.destroy() - yProvider.off("sync", onSync) - } - }, [editorRef, room, activeFileContent]) + // Cleanup logic + if (binding) { + binding.destroy(); + } + if (providerData.binding) { + providerData.binding = undefined; + } + }; + }, [editorRef, room, activeFileContent, activeFileId, tabs]); + + // Added this effect to clean up when the component unmounts + useEffect(() => { + return () => { + // Clean up all providers when the component unmounts + providersMap.current.forEach((data) => { + if (data.binding) { + data.binding.destroy(); + } + data.provider.disconnect(); + data.yDoc.destroy(); + }); + providersMap.current.clear(); + }; + }, []); // Connection/disconnection effect useEffect(() => { From 7a80734c2575b0818a4d5bc61737e4803c7e1fa5 Mon Sep 17 00:00:00 2001 From: Akhilesh Rangani Date: Mon, 15 Jul 2024 15:32:40 -0400 Subject: [PATCH 2/4] fix: remove extra state variables from useEffect --- frontend/components/editor/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/editor/index.tsx b/frontend/components/editor/index.tsx index 45d4962..46fb9ab 100644 --- a/frontend/components/editor/index.tsx +++ b/frontend/components/editor/index.tsx @@ -397,7 +397,7 @@ export default function CodeEditor({ providerData.binding = undefined; } }; - }, [editorRef, room, activeFileContent, activeFileId, tabs]); + }, [editorRef, room, activeFileContent]); // Added this effect to clean up when the component unmounts useEffect(() => { From db1410f587d3af7b615f0bd93971a23d1e79dded Mon Sep 17 00:00:00 2001 From: Akhilesh Rangani Date: Mon, 15 Jul 2024 16:12:08 -0400 Subject: [PATCH 3/4] fix: remove editorRef from useEffect --- frontend/components/editor/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/editor/index.tsx b/frontend/components/editor/index.tsx index 46fb9ab..495951e 100644 --- a/frontend/components/editor/index.tsx +++ b/frontend/components/editor/index.tsx @@ -397,7 +397,7 @@ export default function CodeEditor({ providerData.binding = undefined; } }; - }, [editorRef, room, activeFileContent]); + }, [room, activeFileContent]); // Added this effect to clean up when the component unmounts useEffect(() => { From 08d562ee54b009f76fc8ddf620d0f429f5184095 Mon Sep 17 00:00:00 2001 From: James Murdza Date: Thu, 4 Jul 2024 20:18:36 -0400 Subject: [PATCH 4/4] chore: remove unused variable reactDefinitionFile --- frontend/app/(app)/code/[id]/page.tsx | 11 ----------- frontend/components/editor/index.tsx | 2 -- 2 files changed, 13 deletions(-) diff --git a/frontend/app/(app)/code/[id]/page.tsx b/frontend/app/(app)/code/[id]/page.tsx index 1fd90cc..d97025b 100644 --- a/frontend/app/(app)/code/[id]/page.tsx +++ b/frontend/app/(app)/code/[id]/page.tsx @@ -63,14 +63,6 @@ const CodeEditor = dynamic(() => import("@/components/editor"), { loading: () => , }) -function getReactDefinitionFile() { - const reactDefinitionFile = fs.readFileSync( - "node_modules/@types/react/index.d.ts", - "utf8" - ) - return reactDefinitionFile -} - export default async function CodePage({ params }: { params: { id: string } }) { const user = await currentUser() const sandboxId = params.id @@ -94,8 +86,6 @@ export default async function CodePage({ params }: { params: { id: string } }) { return notFound() } - const reactDefinitionFile = getReactDefinitionFile() - return (
@@ -104,7 +94,6 @@ export default async function CodePage({ params }: { params: { id: string } }) {
diff --git a/frontend/components/editor/index.tsx b/frontend/components/editor/index.tsx index 495951e..30c8d60 100644 --- a/frontend/components/editor/index.tsx +++ b/frontend/components/editor/index.tsx @@ -35,11 +35,9 @@ import { ImperativePanelHandle } from "react-resizable-panels" export default function CodeEditor({ userData, sandboxData, - reactDefinitionFile, }: { userData: User sandboxData: Sandbox - reactDefinitionFile: string }) { const socketRef = useRef(null);