1- import { cn } from "@/lib/utils" ;
2-
3- import { useCallback , useMemo } from "react" ;
1+ import { useCallback , useMemo , useRef , useEffect } from "react" ;
42import {
53 ReactFlow ,
64 MiniMap ,
@@ -11,15 +9,19 @@ import {
119 addEdge ,
1210 BackgroundVariant ,
1311 ConnectionMode ,
14- type OnConnect ,
1512 type Node ,
13+ NodeChange ,
14+ Edge ,
15+ EdgeChange ,
16+ Connection ,
1617} from "@xyflow/react" ;
17-
1818import "@xyflow/react/dist/style.css" ;
19-
20- import { useEffect } from "react" ;
2119import { usePages } from "@/hooks/usePages" ;
2220import { NoteNode } from "./NoteNode" ;
21+ import * as Y from "yjs" ;
22+ import { WebsocketProvider } from "y-websocket" ;
23+ import { cn } from "@/lib/utils" ;
24+ import { useQueryClient } from "@tanstack/react-query" ;
2325
2426const proOptions = { hideAttribution : true } ;
2527
@@ -29,26 +31,151 @@ interface CanvasProps {
2931
3032export default function Canvas ( { className } : CanvasProps ) {
3133 const [ nodes , setNodes , onNodesChange ] = useNodesState < Node > ( [ ] ) ;
32- const [ edges , setEdges , onEdgesChange ] = useEdgesState ( [ ] ) ;
33-
34+ const [ edges , setEdges , onEdgesChange ] = useEdgesState < Edge > ( [ ] ) ;
3435 const { pages } = usePages ( ) ;
36+ const queryClient = useQueryClient ( ) ;
37+
38+ const ydoc = useRef < Y . Doc > ( ) ;
39+ const provider = useRef < WebsocketProvider > ( ) ;
40+ const existingPageIds = useRef ( new Set < string > ( ) ) ;
41+
42+ useEffect ( ( ) => {
43+ const doc = new Y . Doc ( ) ;
44+ const wsProvider = new WebsocketProvider (
45+ "ws://localhost:1234" ,
46+ "flow-room" ,
47+ doc ,
48+ ) ;
49+
50+ ydoc . current = doc ;
51+ provider . current = wsProvider ;
52+
53+ const nodesMap = doc . getMap ( "nodes" ) ;
54+ const edgesMap = doc . getMap ( "edges" ) ;
55+
56+ nodesMap . observe ( ( event ) => {
57+ event . changes . keys . forEach ( ( change , key ) => {
58+ const nodeId = key ;
59+ if ( change . action === "add" || change . action === "update" ) {
60+ const node = nodesMap . get ( nodeId ) as Node ;
61+
62+ if ( change . action === "add" ) {
63+ queryClient . invalidateQueries ( { queryKey : [ "pages" ] } ) ;
64+ }
65+
66+ setNodes ( ( nds ) => {
67+ const index = nds . findIndex ( ( n ) => n . id === nodeId ) ;
68+ if ( index === - 1 ) {
69+ return [ ...nds , node ] ;
70+ }
71+ const newNodes = [ ...nds ] ;
72+ newNodes [ index ] = node ;
73+ return newNodes ;
74+ } ) ;
75+ } else if ( change . action === "delete" ) {
76+ setNodes ( ( nds ) => nds . filter ( ( n ) => n . id !== nodeId ) ) ;
77+ queryClient . invalidateQueries ( { queryKey : [ "pages" ] } ) ;
78+ }
79+ } ) ;
80+ } ) ;
81+
82+ edgesMap . observe ( ( ) => {
83+ const yEdges = Array . from ( edgesMap . values ( ) ) as Edge [ ] ;
84+ setEdges ( yEdges ) ;
85+ } ) ;
86+
87+ return ( ) => {
88+ wsProvider . destroy ( ) ;
89+ doc . destroy ( ) ;
90+ } ;
91+ } , [ queryClient ] ) ;
3592
3693 useEffect ( ( ) => {
37- if ( ! pages ) {
38- return ;
39- }
40-
41- const newNodes = pages . map ( ( page , index ) => ( {
42- id : page . id . toString ( ) ,
43- position : { x : 100 * index , y : 100 } ,
44- data : { title : page . title , id : page . id } ,
45- type : "note" ,
46- } ) ) ;
47- setNodes ( newNodes ) ;
48- } , [ pages , setNodes ] ) ;
49-
50- const onConnect : OnConnect = useCallback (
51- ( params ) => setEdges ( ( eds ) => addEdge ( params , eds ) ) ,
94+ if ( ! pages || ! ydoc . current ) return ;
95+
96+ const nodesMap = ydoc . current . getMap ( "nodes" ) ;
97+ const currentPageIds = new Set ( pages . map ( ( page ) => page . id . toString ( ) ) ) ;
98+
99+ existingPageIds . current . forEach ( ( pageId ) => {
100+ if ( ! currentPageIds . has ( pageId ) ) {
101+ nodesMap . delete ( pageId ) ;
102+ existingPageIds . current . delete ( pageId ) ;
103+ }
104+ } ) ;
105+
106+ pages . forEach ( ( page ) => {
107+ const pageId = page . id . toString ( ) ;
108+ if ( ! existingPageIds . current . has ( pageId ) ) {
109+ const newNode = {
110+ id : pageId ,
111+ position : {
112+ x : Math . random ( ) * 500 ,
113+ y : Math . random ( ) * 500 ,
114+ } ,
115+ data : { title : page . title , id : page . id } ,
116+ type : "note" ,
117+ } ;
118+
119+ nodesMap . set ( pageId , newNode ) ;
120+ existingPageIds . current . add ( pageId ) ;
121+ }
122+ } ) ;
123+ } , [ pages ] ) ;
124+
125+ const handleNodesChange = useCallback (
126+ ( changes : NodeChange [ ] ) => {
127+ if ( ! ydoc . current ) return ;
128+ const nodesMap = ydoc . current . getMap ( "nodes" ) ;
129+
130+ onNodesChange ( changes ) ;
131+
132+ changes . forEach ( ( change ) => {
133+ if ( change . type === "position" && change . position ) {
134+ const node = nodes . find ( ( n ) => n . id === change . id ) ;
135+ if ( node ) {
136+ const updatedNode = {
137+ ...node ,
138+ position : change . position ,
139+ } ;
140+ nodesMap . set ( change . id , updatedNode ) ;
141+ }
142+ }
143+ } ) ;
144+ } ,
145+ [ nodes , onNodesChange ] ,
146+ ) ;
147+
148+ const handleEdgesChange = useCallback (
149+ ( changes : EdgeChange [ ] ) => {
150+ if ( ! ydoc . current ) return ;
151+ const edgesMap = ydoc . current . getMap ( "edges" ) ;
152+
153+ changes . forEach ( ( change ) => {
154+ if ( change . type === "remove" ) {
155+ edgesMap . delete ( change . id ) ;
156+ }
157+ } ) ;
158+
159+ onEdgesChange ( changes ) ;
160+ } ,
161+ [ onEdgesChange ] ,
162+ ) ;
163+
164+ const onConnect = useCallback (
165+ ( connection : Connection ) => {
166+ if ( ! connection . source || ! connection . target || ! ydoc . current ) return ;
167+
168+ const newEdge : Edge = {
169+ id : `e${ connection . source } -${ connection . target } ` ,
170+ source : connection . source ,
171+ target : connection . target ,
172+ sourceHandle : connection . sourceHandle || undefined ,
173+ targetHandle : connection . targetHandle || undefined ,
174+ } ;
175+
176+ ydoc . current . getMap ( "edges" ) . set ( newEdge . id , newEdge ) ;
177+ setEdges ( ( eds ) => addEdge ( connection , eds ) ) ;
178+ } ,
52179 [ setEdges ] ,
53180 ) ;
54181
@@ -59,8 +186,8 @@ export default function Canvas({ className }: CanvasProps) {
59186 < ReactFlow
60187 nodes = { nodes }
61188 edges = { edges }
62- onNodesChange = { onNodesChange }
63- onEdgesChange = { onEdgesChange }
189+ onNodesChange = { handleNodesChange }
190+ onEdgesChange = { handleEdgesChange }
64191 onConnect = { onConnect }
65192 proOptions = { proOptions }
66193 nodeTypes = { nodeTypes }
0 commit comments