1+ "use client"
2+
3+ import { Game } from "@/draw/Game" ;
4+ import { BgFill , canvasBgLight , StrokeEdge , StrokeFill , StrokeStyle , StrokeWidth , ToolType } from "@/types/canvas" ;
5+ import React , { useCallback , useEffect , useRef , useState } from "react" ;
6+ import { Scale } from "../Scale" ;
7+ import { MobileNavbar } from "../mobile-navbar" ;
8+ import { useTheme } from "next-themes" ;
9+ import { MainMenuStack } from "../MainMenuStack" ;
10+ import { ToolMenuStack } from "../ToolMenuStack" ;
11+ import SidebarTriggerButton from "../SidebarTriggerButton" ;
12+ import { useMediaQuery } from "@/hooks/useMediaQuery" ;
13+ import Toolbar from "../Toolbar" ;
14+ import ScreenLoading from "../ScreenLoading" ;
15+ import CollaborationStart from "../CollaborationStartBtn" ;
16+ import { cn } from "@/lib/utils" ;
17+ import { RoomParticipants } from "@repo/common/types" ;
18+
19+ // const WS_URL = process.env.NEXT_PUBLIC_WS_URL || 'ws://localhost:8080';
20+
21+ export default function CanvasSheet ( { roomName, roomId, userId, userName, socket, isConnected } : {
22+ roomName : string ; roomId : string ; userId : string ; userName : string ; token : string ; socket : WebSocket | null ; isConnected : boolean ;
23+ } ) {
24+ const { theme } = useTheme ( )
25+ const canvasRef = useRef < HTMLCanvasElement > ( null ) ;
26+ // const [game, setGame] = useState<Game>();
27+ const [ scale , setScale ] = useState < number > ( 1 ) ;
28+ const [ activeTool , setActiveTool ] = useState < ToolType > ( "grab" ) ;
29+ const [ strokeFill , setStrokeFill ] = useState < StrokeFill > ( "#f08c00" ) ;
30+ const [ strokeWidth , setStrokeWidth ] = useState < StrokeWidth > ( 1 ) ;
31+ const [ bgFill , setBgFill ] = useState < BgFill > ( "#00000000" ) ;
32+ const [ strokeEdge , setStrokeEdge ] = useState < StrokeEdge > ( "round" ) ;
33+ const [ strokeStyle , setStrokeStyle ] = useState < StrokeStyle > ( "solid" ) ;
34+ const [ grabbing , setGrabbing ] = useState ( false ) ;
35+ const paramsRef = useRef ( { roomId, roomName, userId, userName } ) ;
36+ const activeToolRef = useRef ( activeTool ) ;
37+ const strokeFillRef = useRef ( strokeFill ) ;
38+ const strokeWidthRef = useRef ( strokeWidth ) ;
39+ const strokeEdgeRef = useRef ( strokeEdge ) ;
40+ const strokeStyleRef = useRef ( strokeStyle ) ;
41+ const bgFillRef = useRef ( bgFill ) ;
42+ const [ sidebarOpen , setSidebarOpen ] = useState ( false ) ;
43+ const [ canvasColor , setCanvasColor ] = useState < string > ( canvasBgLight [ 0 ] ) ;
44+ const canvasColorRef = useRef ( canvasColor ) ;
45+ const { matches, isLoading } = useMediaQuery ( 'md' ) ;
46+ const [ participants , setParticipants ] = useState < RoomParticipants [ ] > ( [ ] ) ;
47+ const gameRef = useRef < Game > ( ) ;
48+ const [ isSocketReady , setIsSocketReady ] = useState ( false ) ;
49+ const connectionRef = useRef < number > ( 0 ) ;
50+
51+ useEffect ( ( ) => {
52+ if ( ! socket ) {
53+ setIsSocketReady ( false ) ;
54+ return ;
55+ }
56+
57+ const currentConnection = ++ connectionRef . current ;
58+ let timeout : NodeJS . Timeout ;
59+
60+ const handleOpen = ( ) => {
61+ if ( currentConnection !== connectionRef . current ) return ;
62+ console . log ( 'Socket ready in CanvasSheet' ) ;
63+ setIsSocketReady ( true ) ;
64+ } ;
65+
66+ if ( socket . readyState === WebSocket . OPEN ) {
67+ // Add slight delay to ensure parent state updates
68+ timeout = setTimeout ( handleOpen , 50 ) ;
69+ } else {
70+ socket . addEventListener ( 'open' , handleOpen ) ;
71+ }
72+
73+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
74+ let connectionRefValue = connectionRef . current ;
75+
76+ return ( ) => {
77+ connectionRefValue ++ ;
78+ clearTimeout ( timeout ) ;
79+ socket . removeEventListener ( 'open' , handleOpen ) ;
80+ setIsSocketReady ( false ) ;
81+ } ;
82+ } , [ socket , isConnected , setIsSocketReady ] ) ; // Add isConnected to dependencies
83+
84+ useEffect ( ( ) => {
85+ console . log ( 'E2' )
86+ paramsRef . current = { roomId, roomName, userId, userName } ;
87+ } , [ roomId , roomName , userId , userName ] ) ;
88+
89+ useEffect ( ( ) => {
90+ console . log ( 'E3' )
91+ setCanvasColor ( canvasBgLight [ 0 ] ) ;
92+ } , [ theme ] )
93+
94+ useEffect ( ( ) => {
95+ console . log ( 'E4' )
96+ const handleResize = ( ) => {
97+ if ( canvasRef . current && gameRef . current ) {
98+ const canvas = canvasRef . current ;
99+ canvas . width = window . innerWidth ;
100+ canvas . height = window . innerHeight ;
101+ gameRef . current . handleResize ( window . innerWidth , window . innerHeight ) ;
102+ }
103+ } ;
104+ handleResize ( ) ;
105+ window . addEventListener ( 'resize' , handleResize ) ;
106+ return ( ) => window . removeEventListener ( 'resize' , handleResize ) ;
107+ } , [ gameRef ] ) ;
108+
109+ useEffect ( ( ) => {
110+ console . log ( 'E6' )
111+ strokeEdgeRef . current = strokeEdge ;
112+ gameRef ?. current ?. setStrokeEdge ( strokeEdge ) ;
113+ } , [ strokeEdge , gameRef ] ) ;
114+
115+ useEffect ( ( ) => {
116+ console . log ( 'E7' )
117+ strokeStyleRef . current = strokeStyle ;
118+ gameRef ?. current ?. setStrokeStyle ( strokeStyle ) ;
119+ } , [ strokeStyle , gameRef ] ) ;
120+
121+ useEffect ( ( ) => {
122+ console . log ( 'E8' )
123+ activeToolRef . current = activeTool ;
124+ gameRef ?. current ?. setTool ( activeTool ) ;
125+ } , [ activeTool , gameRef ] ) ;
126+
127+ useEffect ( ( ) => {
128+ console . log ( 'E9' )
129+ strokeWidthRef . current = strokeWidth ;
130+ gameRef ?. current ?. setStrokeWidth ( strokeWidth ) ;
131+ } , [ strokeWidth , gameRef ] ) ;
132+
133+ useEffect ( ( ) => {
134+ console . log ( 'E10' )
135+ strokeFillRef . current = strokeFill ;
136+ gameRef ?. current ?. setStrokeFill ( strokeFill ) ;
137+ } , [ strokeFill , gameRef ] ) ;
138+
139+ useEffect ( ( ) => {
140+ console . log ( 'E11' )
141+ bgFillRef . current = bgFill ;
142+ gameRef ?. current ?. setBgFill ( bgFill ) ;
143+ } , [ bgFill , gameRef ] ) ;
144+
145+ useEffect ( ( ) => {
146+ console . log ( 'E12' )
147+ if ( gameRef . current && canvasColorRef . current !== canvasColor ) {
148+ canvasColorRef . current = canvasColor ;
149+ gameRef . current . setCanvasBgColor ( canvasColor ) ;
150+ }
151+ } , [ canvasColor , gameRef ] ) ;
152+
153+ useEffect ( ( ) => {
154+ console . log ( 'E13' )
155+ if ( gameRef . current ) {
156+ gameRef . current . setScale ( scale ) ;
157+ }
158+ } , [ scale , gameRef ] ) ;
159+
160+ useEffect ( ( ) => {
161+ console . log ( 'E14' )
162+ const handleKeyDown = ( e : KeyboardEvent ) => {
163+ switch ( e . key ) {
164+ case "1" :
165+ setActiveTool ( "grab" ) ;
166+ break ;
167+ case "2" :
168+ setActiveTool ( "rectangle" ) ;
169+ break ;
170+ case "3" :
171+ setActiveTool ( "ellipse" ) ;
172+ break ;
173+ case "4" :
174+ setActiveTool ( "diamond" ) ;
175+ break ;
176+ case "5" :
177+ setActiveTool ( "line" ) ;
178+ break ;
179+ case "6" :
180+ setActiveTool ( "pen" ) ;
181+ break ;
182+ case "7" :
183+ setActiveTool ( "eraser" ) ;
184+ break ;
185+ default :
186+ break ;
187+ }
188+ } ;
189+ document . addEventListener ( "keydown" , handleKeyDown ) ;
190+ return ( ) => {
191+ document . removeEventListener ( "keydown" , handleKeyDown ) ;
192+ } ;
193+ } , [ ] ) ;
194+
195+ useEffect ( ( ) => {
196+ console . log ( 'E16' )
197+ if ( ! isSocketReady || ! socket || ! canvasRef . current ) return ;
198+ console . log ( 'Initializing game with valid socket' ) ;
199+ const game = new Game (
200+ canvasRef . current ,
201+ paramsRef . current . roomId ,
202+ canvasColorRef . current ,
203+ paramsRef . current . roomName ,
204+ ( newScale ) => setScale ( newScale ) ,
205+ false ,
206+ socket ,
207+ ( newParticipants ) => setParticipants ( newParticipants )
208+ ) ;
209+
210+ gameRef . current = game ;
211+ // setGame(game);
212+
213+ canvasRef . current . width = window . innerWidth ;
214+ canvasRef . current . height = window . innerHeight ;
215+
216+ return ( ) => {
217+ gameRef . current ?. destroy ( ) ;
218+ gameRef . current = undefined ;
219+ } ;
220+ } , [ isSocketReady , socket ] ) ;
221+
222+ useEffect ( ( ) => {
223+ console . log ( 'E17' )
224+ if ( activeTool === "grab" ) {
225+ const handleGrab = ( ) => {
226+ setGrabbing ( ( prev ) => ! prev )
227+ }
228+
229+ document . addEventListener ( "mousedown" , handleGrab )
230+ document . addEventListener ( "mouseup" , handleGrab )
231+
232+ return ( ) => {
233+ document . removeEventListener ( "mousedown" , handleGrab )
234+ document . removeEventListener ( "mouseup" , handleGrab )
235+ }
236+ }
237+ } , [ activeTool ] ) ;
238+
239+ const toggleSidebar = useCallback ( ( ) => {
240+ console . log ( 'E19' )
241+ setSidebarOpen ( prev => ! prev ) ;
242+ } , [ ] ) ;
243+
244+ // useEffect(() => {
245+ // if (!socket) {
246+ // setIsSocketReady(false);
247+ // return;
248+ // }
249+
250+ // const handleOpen = () => setIsSocketReady(true);
251+
252+ // if (socket.readyState === WebSocket.OPEN) {
253+ // handleOpen();
254+ // } else {
255+ // socket.addEventListener('open', handleOpen);
256+ // }
257+
258+ // console.log('isSocketReady = ', isSocketReady)
259+
260+ // return () => {
261+ // socket.removeEventListener('open', handleOpen);
262+ // };
263+ // }, [isSocketReady, socket]);
264+
265+ // useEffect(() => {
266+ // console.log('socket in CanvasSheet = ', socket)
267+ // }, [socket])
268+
269+ useEffect ( ( ) => {
270+ console . log ( 'participants = ' , participants )
271+ } , [ participants ] )
272+
273+ if ( isLoading ) {
274+ return < ScreenLoading />
275+ }
276+
277+ if ( ! socket || ! isSocketReady ) {
278+ return < ScreenLoading content = { isConnected ? "Finalizing connection..." : "Connecting..." } /> ;
279+ }
280+
281+ return (
282+ < div className = { cn ( "collabydraw h-screen overflow-hidden" ,
283+ activeTool === "eraser"
284+ ? "cursor-[url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAOBJREFUOE9jZKAyYKSyeQzDwMD////7MDAw6EGD5hIjI+MWfMGE08sggz5+/Dj71q1bHPv27eMFGeLk5PRZTU3tBz8/fyoug7EaCDLs58+fa0NDQ9k2b96M4iBfX1+G1atX/2JnZw/GZihWAz98+PA8NjZWAt0wmMkgQxcvXvxCQEBAEt37GAaCXHf69OnFZmZmAvjC6tSpUx9MTU1j0V2JzcCqzs7OpoqKCmZ8BnZ0dPwtLy+vY2RkbENWRxcDqetlkPOpGikgA6mebGCGUi1hI8ca1bIeucXaMCi+SPU6AHRTjhWg+vuGAAAAAElFTkSuQmCC')_10_10,auto]"
285+ : activeTool === "grab" && ! sidebarOpen
286+ ? grabbing ? "cursor-grabbing" : "cursor-grab"
287+ : "cursor-crosshair" ) } >
288+ < div className = "App_Menu App_Menu_Top fixed z-[4] top-4 right-4 left-4 flex justify-center items-center md:grid md:grid-cols-[1fr_auto_1fr] md:gap-8 md:items-start" >
289+ { matches && (
290+ < div className = "Main_Menu_Stack Sidebar_Trigger_Button md:grid md:gap-[calc(.25rem*6)] grid-cols-[auto] grid-flow-row grid-rows auto-rows-min justify-self-start" >
291+ < div className = "relative" >
292+ < SidebarTriggerButton onClick = { toggleSidebar } />
293+
294+ { sidebarOpen && (
295+ < MainMenuStack
296+ isOpen = { sidebarOpen }
297+ onClose = { ( ) => setSidebarOpen ( false ) }
298+ canvasColor = { canvasColor }
299+ setCanvasColor = { setCanvasColor }
300+ roomName = { roomName }
301+ />
302+ ) }
303+ </ div >
304+
305+ < ToolMenuStack activeTool = { activeTool }
306+ strokeFill = { strokeFill }
307+ setStrokeFill = { setStrokeFill }
308+ strokeWidth = { strokeWidth }
309+ setStrokeWidth = { setStrokeWidth }
310+ bgFill = { bgFill }
311+ setBgFill = { setBgFill }
312+ strokeEdge = { strokeEdge }
313+ setStrokeEdge = { setStrokeEdge }
314+ strokeStyle = { strokeStyle }
315+ setStrokeStyle = { setStrokeStyle }
316+ />
317+ </ div >
318+ ) }
319+ < Toolbar
320+ selectedTool = { activeTool }
321+ onToolSelect = { setActiveTool }
322+ />
323+ { matches && (
324+ < CollaborationStart participants = { participants } slug = { roomName } />
325+ ) }
326+ </ div >
327+
328+ { matches && (
329+ < Scale scale = { scale } setScale = { setScale } />
330+ ) }
331+
332+ { ! matches && (
333+ < MobileNavbar
334+ sidebarOpen = { sidebarOpen }
335+ setSidebarOpen = { setSidebarOpen }
336+ canvasColor = { canvasColor }
337+ setCanvasColor = { setCanvasColor }
338+ scale = { scale }
339+ setScale = { setScale }
340+
341+ activeTool = { activeTool }
342+ strokeFill = { strokeFill }
343+ setStrokeFill = { setStrokeFill }
344+ strokeWidth = { strokeWidth }
345+ setStrokeWidth = { setStrokeWidth }
346+ bgFill = { bgFill }
347+ setBgFill = { setBgFill }
348+
349+ strokeEdge = { strokeEdge }
350+ setStrokeEdge = { setStrokeEdge }
351+ strokeStyle = { strokeStyle }
352+ setStrokeStyle = { setStrokeStyle }
353+
354+ roomName = { roomName }
355+ />
356+ ) }
357+ < canvas className = { cn ( "collabydraw collabydraw-canvas" , theme === 'dark' ? 'collabydraw-canvas-dark' : '' ) } ref = { canvasRef } />
358+ </ div >
359+ )
360+ } ;
0 commit comments