@@ -21,16 +21,17 @@ export const Canvas = () => {
2121 const [roomId, setRoomId] = useState("");
2222 const [joined, setJoined] = useState(false);
2323 const [socket, setSocket] = useState(null);
24+ const [isModalOpen, setIsModalOpen] = useState(false); // controls modal visibility
2425
2526 // Initialize socket connection
2627 useEffect(() => {
27- const s = io("http://localhost:3000"); // replace with your backend URL
28+ const s = io("http://localhost:3000");
2829 setSocket(s);
2930
3031 s.on("connect", () => console.log("Connected to server:", s.id));
3132
32- // Handle strokes from other users
3333 s.on("draw", ({ x, y, color, width, type, tool }) => {
34+ if (!joined) return; // only listen when in collab mode
3435 const ctx = canvasRef.current?.getContext("2d");
3536 if (!ctx) return;
3637
@@ -42,7 +43,7 @@ export const Canvas = () => {
4243 });
4344
4445 return () => s.disconnect();
45- }, []);
46+ }, [joined ]);
4647
4748 // Canvas setup
4849 useEffect(() => {
@@ -62,7 +63,7 @@ export const Canvas = () => {
6263 };
6364
6465 window.addEventListener("resize", handleResize);
65- toast.success("Canvas ready! Start drawing! ");
66+ toast.success("Canvas ready! Local mode active. ");
6667 return () => window.removeEventListener("resize", handleResize);
6768 }, []);
6869
@@ -86,10 +87,6 @@ export const Canvas = () => {
8687
8788 // Drawing logic
8889 const startDrawing = (e) => {
89- if (!joined) {
90- toast.error("Join a room first!");
91- return;
92- }
9390 const canvas = canvasRef.current;
9491 const ctx = canvas?.getContext("2d");
9592 if (!ctx) return;
@@ -104,15 +101,17 @@ export const Canvas = () => {
104101 if (activeTool === "pen" || activeTool === "eraser") {
105102 ctx.beginPath();
106103 ctx.moveTo(x, y);
107- socket.emit("draw", {
108- roomId,
109- x,
110- y,
111- type: "start",
112- color: activeColor,
113- width: strokeWidth,
114- tool: activeTool,
115- });
104+
105+ if (joined && socket)
106+ socket.emit("draw", {
107+ roomId,
108+ x,
109+ y,
110+ type: "start",
111+ color: activeColor,
112+ width: strokeWidth,
113+ tool: activeTool,
114+ });
116115 }
117116
118117 if (activeTool === "line") {
@@ -136,15 +135,16 @@ export const Canvas = () => {
136135 ctx.lineTo(x, y);
137136 ctx.stroke();
138137
139- socket.emit("draw", {
140- roomId,
141- x,
142- y,
143- type: "move",
144- color: activeColor,
145- width: strokeWidth,
146- tool: activeTool,
147- });
138+ if (joined && socket)
139+ socket.emit("draw", {
140+ roomId,
141+ x,
142+ y,
143+ type: "move",
144+ color: activeColor,
145+ width: strokeWidth,
146+ tool: activeTool,
147+ });
148148 } else if (activeTool === "line") {
149149 ctx.putImageData(snapshot.current, 0, 0);
150150 ctx.beginPath();
@@ -174,21 +174,43 @@ export const Canvas = () => {
174174 if (!roomId.trim() || !socket) return;
175175 socket.emit("join-room", roomId.trim());
176176 setJoined(true);
177- toast.success(`Joined room: ${roomId}`);
177+ setIsModalOpen(false);
178+ toast.success(`Collaborative mode active - joined room: ${roomId}`);
178179 };
179180
180181 const handleToolChange = (tool) => {
181182 setActiveTool(tool);
182183 toast.info(`${tool.charAt(0).toUpperCase() + tool.slice(1)} tool selected`);
183184 };
184185
186+ const handleExitRoom = () => {
187+ if (!socket) return;
188+ socket.emit("leave-room", roomId);
189+ setJoined(false);
190+ toast.success(`Left room: ${roomId}`);
191+ };
192+
185193 return (
186194 <div className="relative w-full h-screen overflow-hidden bg-canvas">
187195 <Toolbar
188196 activeTool={activeTool}
189197 onToolChange={handleToolChange}
190198 onClear={handleClear}
191199 />
200+
201+ {joined ? <button
202+ onClick={() => handleExitRoom()}
203+ className="fixed top-4 right-4 bg-red-700 text-white px-4 py-2 rounded-lg shadow hover:bg-red-800 z-50"
204+ >
205+ Exit Room
206+ </button> : <button
207+ onClick={() => setIsModalOpen(true)}
208+ className="fixed top-4 right-4 bg-blue-600 text-white px-4 py-2 rounded-lg shadow hover:bg-blue-700 z-50"
209+ >
210+ Collaborate
211+ </button>}
212+
213+
192214 <ColorPicker activeColor={activeColor} onColorChange={setActiveColor} />
193215 <StrokeControl
194216 strokeWidth={strokeWidth}
@@ -207,7 +229,8 @@ export const Canvas = () => {
207229 className="cursor-crosshair focus:outline-2 focus:outline-primary"
208230 />
209231
210- {!joined && (
232+ {/* Modal only appears when user clicks Collaborate */}
233+ {isModalOpen && (
211234 <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">
212235 <h2 className="text-xl font-semibold">Join a Room</h2>
213236 <input
@@ -223,13 +246,21 @@ export const Canvas = () => {
223246 >
224247 Join Room
225248 </button>
249+ <button
250+ onClick={() => setIsModalOpen(false)}
251+ className="text-sm text-gray-600 mt-2 hover:underline"
252+ >
253+ Cancel
254+ </button>
226255 </div>
227256 )}
228257
229258 <div className="fixed bottom-6 left-1/2 -translate-x-1/2 pointer-events-none">
230259 <div className="bg-toolbar/95 border border-toolbar-border rounded-xl shadow-lg backdrop-blur-sm px-6 py-3">
231260 <p className="text-sm text-foreground font-medium">
232- Welcome to CollabCanvas — Select a tool and start drawing!
261+ {joined
262+ ? `Connected to Room: ${roomId}`
263+ : "Local mode - your drawings are not shared."}
233264 </p>
234265 </div>
235266 </div>
0 commit comments