@@ -13,39 +13,32 @@ export const Canvas = () => {
1313 const [ isDrawing , setIsDrawing ] = useState ( false ) ;
1414 const [ isCanvasFocused , setIsCanvasFocused ] = useState ( false ) ;
1515
16- // For line tool
1716 const startPoint = useRef ( { x : 0 , y : 0 } ) ;
1817 const snapshot = useRef ( null ) ;
1918
2019 // Collaboration
2120 const [ roomId , setRoomId ] = useState ( "" ) ;
22- const [ joined , setJoined ] = useState ( false ) ; // false = local mode
21+ const [ joined , setJoined ] = useState ( false ) ;
2322 const [ socket , setSocket ] = useState ( null ) ;
24- const [ isModalOpen , setIsModalOpen ] = useState ( false ) ; // controls modal visibility
23+ const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
2524
26- // Initialize socket connection (always available but idle until joined)
2725 useEffect ( ( ) => {
2826 const s = io ( "http://localhost:3000" ) ;
2927 setSocket ( s ) ;
30-
3128 s . on ( "connect" , ( ) => console . log ( "Connected to server:" , s . id ) ) ;
32-
3329 s . on ( "draw" , ( { x, y, color, width, type, tool } ) => {
34- if ( ! joined ) return ; // only listen when in collab mode
30+ if ( ! joined ) return ;
3531 const ctx = canvasRef . current ?. getContext ( "2d" ) ;
3632 if ( ! ctx ) return ;
37-
3833 if ( type === "start" ) ctx . beginPath ( ) ;
3934 ctx . strokeStyle = tool === "eraser" ? "#ffffff" : color ;
4035 ctx . lineWidth = tool === "eraser" ? width * 3 : width ;
4136 ctx . lineTo ( x , y ) ;
4237 ctx . stroke ( ) ;
4338 } ) ;
44-
4539 return ( ) => s . disconnect ( ) ;
4640 } , [ joined ] ) ;
4741
48- // Canvas setup
4942 useEffect ( ( ) => {
5043 const canvas = canvasRef . current ;
5144 if ( ! canvas ) return ;
@@ -54,20 +47,17 @@ export const Canvas = () => {
5447 canvas . height = window . innerHeight ;
5548 ctx . lineCap = "round" ;
5649 ctx . lineJoin = "round" ;
57-
5850 const handleResize = ( ) => {
5951 const image = ctx . getImageData ( 0 , 0 , canvas . width , canvas . height ) ;
6052 canvas . width = window . innerWidth ;
6153 canvas . height = window . innerHeight ;
6254 ctx . putImageData ( image , 0 , 0 ) ;
6355 } ;
64-
6556 window . addEventListener ( "resize" , handleResize ) ;
6657 toast . success ( "Canvas ready! Local mode active." ) ;
6758 return ( ) => window . removeEventListener ( "resize" , handleResize ) ;
6859 } , [ ] ) ;
6960
70- // Keyboard shortcuts
7161 useEffect ( ( ) => {
7262 const handleKeyDown = ( e ) => {
7363 if ( ! isCanvasFocused ) return ;
@@ -79,23 +69,19 @@ export const Canvas = () => {
7969 return ( ) => window . removeEventListener ( "keydown" , handleKeyDown ) ;
8070 } , [ isCanvasFocused ] ) ;
8171
82- // Drawing logic
72+ // Drawing logic handlers
8373 const startDrawing = ( e ) => {
8474 const canvas = canvasRef . current ;
8575 const ctx = canvas ?. getContext ( "2d" ) ;
8676 if ( ! ctx ) return ;
87-
8877 const rect = canvas . getBoundingClientRect ( ) ;
8978 const x = e . clientX - rect . left ;
9079 const y = e . clientY - rect . top ;
91-
9280 startPoint . current = { x, y } ;
9381 setIsDrawing ( true ) ;
94-
9582 if ( activeTool === "pen" || activeTool === "eraser" ) {
9683 ctx . beginPath ( ) ;
9784 ctx . moveTo ( x , y ) ;
98-
9985 if ( joined && socket )
10086 socket . emit ( "draw" , {
10187 roomId,
@@ -107,8 +93,8 @@ export const Canvas = () => {
10793 tool : activeTool ,
10894 } ) ;
10995 }
110-
111- if ( activeTool === "line" ) {
96+ // Save snapshot for preview tools
97+ if ( activeTool === "line" || activeTool === "rectangle" ) {
11298 snapshot . current = ctx . getImageData ( 0 , 0 , canvas . width , canvas . height ) ;
11399 }
114100 } ;
@@ -118,17 +104,14 @@ export const Canvas = () => {
118104 const canvas = canvasRef . current ;
119105 const ctx = canvas ?. getContext ( "2d" ) ;
120106 if ( ! ctx ) return ;
121-
122107 const rect = canvas . getBoundingClientRect ( ) ;
123108 const x = e . clientX - rect . left ;
124109 const y = e . clientY - rect . top ;
125-
126110 if ( activeTool === "pen" || activeTool === "eraser" ) {
127111 ctx . strokeStyle = activeTool === "eraser" ? "#ffffff" : activeColor ;
128112 ctx . lineWidth = activeTool === "eraser" ? strokeWidth * 3 : strokeWidth ;
129113 ctx . lineTo ( x , y ) ;
130114 ctx . stroke ( ) ;
131-
132115 if ( joined && socket )
133116 socket . emit ( "draw" , {
134117 roomId,
@@ -148,6 +131,17 @@ export const Canvas = () => {
148131 ctx . lineWidth = strokeWidth ;
149132 ctx . stroke ( ) ;
150133 ctx . closePath ( ) ;
134+ } else if ( activeTool === "rectangle" ) {
135+ ctx . putImageData ( snapshot . current , 0 , 0 ) ;
136+ ctx . beginPath ( ) ;
137+ const startX = startPoint . current . x ;
138+ const startY = startPoint . current . y ;
139+ const width = x - startX ;
140+ const height = y - startY ;
141+ ctx . strokeStyle = activeColor ;
142+ ctx . lineWidth = strokeWidth ;
143+ ctx . strokeRect ( startX , startY , width , height ) ;
144+ ctx . closePath ( ) ;
151145 }
152146 } ;
153147
@@ -192,27 +186,26 @@ export const Canvas = () => {
192186 onToolChange = { handleToolChange }
193187 onClear = { handleClear }
194188 />
195-
196- { joined ? < button
197- onClick = { ( ) => handleExitRoom ( ) }
198- className = "fixed top-4 right-4 bg-red-700 text-white px-4 py-2 rounded-lg shadow hover:bg-red-800 z-50"
199- >
200- Exit Room
201- </ button >
202- : < button
203- onClick = { ( ) => setIsModalOpen ( true ) }
204- className = "fixed top-4 right-4 bg-blue-600 text-white px-4 py-2 rounded-lg shadow hover:bg-blue-700 z-50"
205- >
206- Collaborate
207- </ button > }
208-
209-
189+ { joined ? (
190+ < button
191+ onClick = { ( ) => handleExitRoom ( ) }
192+ className = "fixed top-4 right-4 bg-red-700 text-white px-4 py-2 rounded-lg shadow hover:bg-red-800 z-50"
193+ >
194+ Exit Room
195+ </ button >
196+ ) : (
197+ < button
198+ onClick = { ( ) => setIsModalOpen ( true ) }
199+ className = "fixed top-4 right-4 bg-blue-600 text-white px-4 py-2 rounded-lg shadow hover:bg-blue-700 z-50"
200+ >
201+ Collaborate
202+ </ button >
203+ ) }
210204 < ColorPicker activeColor = { activeColor } onColorChange = { setActiveColor } />
211205 < StrokeControl
212206 strokeWidth = { strokeWidth }
213207 onStrokeWidthChange = { setStrokeWidth }
214208 />
215-
216209 < canvas
217210 ref = { canvasRef }
218211 tabIndex = { 0 }
@@ -224,7 +217,6 @@ export const Canvas = () => {
224217 onMouseLeave = { stopDrawing }
225218 className = "cursor-crosshair focus:outline-2 focus:outline-primary"
226219 />
227-
228220 { isModalOpen && (
229221 < div className = "fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white/90 border border-gray-400 rounded-xl shadow-xl p-6 z-50 flex flex-col items-center gap-3" >
230222 < h2 className = "text-xl font-semibold" > Join a Room</ h2 >
@@ -249,7 +241,6 @@ export const Canvas = () => {
249241 </ button >
250242 </ div >
251243 ) }
252-
253244 < div className = "fixed bottom-6 left-1/2 -translate-x-1/2 pointer-events-none" >
254245 < div className = "bg-toolbar/95 border border-toolbar-border rounded-xl shadow-lg backdrop-blur-sm px-6 py-3" >
255246 < p className = "text-sm text-foreground font-medium" >
0 commit comments