From e92e86af77ee0ce726c0c2f8de34181c139980a3 Mon Sep 17 00:00:00 2001 From: Ishaan Dey Date: Sat, 4 May 2024 20:50:00 -0700 Subject: [PATCH] working file sync between local state and liveblocks yjs --- frontend/components/editor/index.tsx | 119 ++++++++++++-------- frontend/components/editor/live/cursors.tsx | 1 - 2 files changed, 69 insertions(+), 51 deletions(-) diff --git a/frontend/components/editor/index.tsx b/frontend/components/editor/index.tsx index 2e23b60..c80bc95 100644 --- a/frontend/components/editor/index.tsx +++ b/frontend/components/editor/index.tsx @@ -75,7 +75,9 @@ export default function CodeEditor({ const clerk = useClerk() const room = useRoom() - const editorRef = useRef(null) + // const editorRef = useRef(null) + const [editorRef, setEditorRef] = + useState() const editorContainerRef = useRef(null) const monacoRef = useRef(null) const generateRef = useRef(null) @@ -92,7 +94,7 @@ export default function CodeEditor({ } const handleEditorMount: OnMount = (editor, monaco) => { - editorRef.current = editor + setEditorRef(editor) monacoRef.current = monaco editor.onDidChangeCursorPosition((e) => { @@ -161,7 +163,7 @@ export default function CodeEditor({ return } if (generate.show) { - editorRef.current?.changeViewZones(function (changeAccessor) { + editorRef?.changeViewZones(function (changeAccessor) { if (!generateRef.current) return const id = changeAccessor.addZone({ afterLineNumber: cursorLine, @@ -197,14 +199,14 @@ export default function CodeEditor({ setGenerate((prev) => { return { ...prev, widget: contentWidget } }) - editorRef.current?.addContentWidget(contentWidget) + editorRef?.addContentWidget(contentWidget) if (generateRef.current && generateWidgetRef.current) { - editorRef.current?.applyFontInfo(generateRef.current) - editorRef.current?.applyFontInfo(generateWidgetRef.current) + editorRef?.applyFontInfo(generateRef.current) + editorRef?.applyFontInfo(generateWidgetRef.current) } } else { - editorRef.current?.changeViewZones(function (changeAccessor) { + editorRef?.changeViewZones(function (changeAccessor) { changeAccessor.removeZone(generate.id) setGenerate((prev) => { return { ...prev, id: "" } @@ -212,7 +214,7 @@ export default function CodeEditor({ }) if (!generate.widget) return - editorRef.current?.removeContentWidget(generate.widget) + editorRef?.removeContentWidget(generate.widget) setGenerate((prev) => { return { ...prev, @@ -232,7 +234,7 @@ export default function CodeEditor({ if (decorations.instance) { decorations.instance.set(decorations.options) } else { - const instance = editorRef.current?.createDecorationsCollection() + const instance = editorRef?.createDecorationsCollection() instance?.set(decorations.options) setDecorations((prev) => { @@ -254,7 +256,7 @@ export default function CodeEditor({ e.preventDefault() // const activeTab = tabs.find((t) => t.id === activeId) - // console.log("saving:", activeTab?.name, editorRef.current?.getValue()) + // console.log("saving:", activeTab?.name, editorRef?.getValue()) setTabs((prev) => prev.map((tab) => @@ -262,7 +264,7 @@ export default function CodeEditor({ ) ) - socket.emit("saveFile", activeId, editorRef.current?.getValue()) + socket.emit("saveFile", activeId, editorRef?.getValue()) } } document.addEventListener("keydown", down) @@ -281,36 +283,51 @@ export default function CodeEditor({ } }) - // useEffect(() => { - // let yProvider: any - // let yDoc: Y.Doc - // let binding: MonacoBinding + useEffect(() => { + const tab = tabs.find((t) => t.id === activeId) + const model = editorRef?.getModel() - // const tab = tabs.find((t) => t.id === activeId) + if (!editorRef || !tab || !model) return - // if (editorRef.current && tab) { - // yDoc = new Y.Doc() - // const yText = yDoc.getText(tab.id) - // yProvider = new LiveblocksProvider(room, yDoc) - // setProvider(yProvider) + const yDoc = new Y.Doc() + const yText = yDoc.getText(tab.id) + const yProvider: any = new LiveblocksProvider(room, yDoc) - // yDoc?.getText(tab.id)?.insert(0, editorRef.current?.getValue() ?? "") + const onSync = (isSynced: boolean) => { + if (isSynced) { + const text = yText.toString() + if (text === "") { + if (activeFile) { + yText.insert(0, activeFile) + } else { + setTimeout(() => { + yText.insert(0, editorRef.getValue()) + }, 0) + } + } + } else { + // Yjs content is not synchronized + } + } - // // Attach Yjs to Monaco - // binding = new MonacoBinding( - // yText, - // editorRef.current.getModel() as monaco.editor.ITextModel, - // new Set([editorRef.current]), - // yProvider.awareness as Awareness - // ) - // } + yProvider.on("sync", onSync) - // return () => { - // yDoc?.destroy() - // yProvider?.destroy() - // binding?.destroy() - // } - // }, [editorRef.current, room, activeId]) + setProvider(yProvider) + + const binding = new MonacoBinding( + yText, + model, + new Set([editorRef]), + yProvider.awareness as Awareness + ) + + return () => { + yDoc.destroy() + yProvider.destroy() + binding.destroy() + yProvider.off("sync", onSync) + } + }, [editorRef, room, activeFile]) // connection/disconnection effect + resizeobserver useEffect(() => { @@ -387,12 +404,10 @@ export default function CodeEditor({ setTabs((prev) => prev.filter((t) => t.id !== tab.id)) if (!nextId) { - console.log("no next id") setActiveId("") } else { const nextTab = tabs.find((t) => t.id === nextId) if (nextTab) { - console.log("next tab:", nextTab.name) selectFile(nextTab) } } @@ -430,10 +445,6 @@ export default function CodeEditor({ // }) } - useEffect(() => { - console.log("activeId CHANGED:", activeId) - }, [activeId]) - return ( <>
@@ -444,14 +455,14 @@ export default function CodeEditor({ width={generate.width - 90} data={{ fileName: tabs.find((t) => t.id === activeId)?.name ?? "", - code: editorRef.current?.getValue() ?? "", + code: editorRef?.getValue() ?? "", line: generate.line, }} editor={{ language: editorLanguage, }} onExpand={() => { - editorRef.current?.changeViewZones(function (changeAccessor) { + editorRef?.changeViewZones(function (changeAccessor) { changeAccessor.removeZone(generate.id) if (!generateRef.current) return @@ -473,12 +484,12 @@ export default function CodeEditor({ show: !prev.show, } }) - const file = editorRef.current?.getValue() + const file = editorRef?.getValue() const lines = file?.split("\n") || [] lines.splice(line - 1, 0, code) const updatedFile = lines.join("\n") - editorRef.current?.setValue(updatedFile) + editorRef?.setValue(updatedFile) }} /> ) : null} @@ -547,11 +558,19 @@ export default function CodeEditor({ beforeMount={handleEditorWillMount} onMount={handleEditorMount} onChange={(value) => { - setTabs((prev) => - prev.map((tab) => - tab.id === activeId ? { ...tab, saved: false } : tab + if (value === activeFile) { + setTabs((prev) => + prev.map((tab) => + tab.id === activeId ? { ...tab, saved: true } : tab + ) ) - ) + } else { + setTabs((prev) => + prev.map((tab) => + tab.id === activeId ? { ...tab, saved: false } : tab + ) + ) + } }} options={{ minimap: { diff --git a/frontend/components/editor/live/cursors.tsx b/frontend/components/editor/live/cursors.tsx index b1269e3..c86dd47 100644 --- a/frontend/components/editor/live/cursors.tsx +++ b/frontend/components/editor/live/cursors.tsx @@ -19,7 +19,6 @@ export function Cursors({ yProvider }: { yProvider: TypedLiveblocksProvider }) { // On changes, update `awarenessUsers` function setUsers() { - console.log("SET USERS") setAwarenessUsers( Array.from(yProvider.awareness.getStates()) as AwarenessList )