Skip to content

Commit 48a3c09

Browse files
feat: refactor App component and enhance Canvas collaboration features
1 parent 28806d5 commit 48a3c09

File tree

2 files changed

+61
-64
lines changed

2 files changed

+61
-64
lines changed

client/src/App.jsx

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,9 @@
1-
import { useState } from "react";
21
import { Canvas } from "./components/Canvas";
32

43
function App() {
5-
const [roomId, setRoomId] = useState("");
6-
const [joined, setJoined] = useState(false);
7-
8-
const handleJoin = () => {
9-
if (!roomId.trim()) {
10-
alert("Please enter a valid Room ID");
11-
return;
12-
}
13-
setJoined(true);
14-
};
154

165
return (
17-
<main className="w-full h-screen flex items-center justify-center bg-gray-100">
18-
{!joined ? (
19-
<div className="flex flex-col items-center gap-4 p-6 bg-white rounded-xl shadow-md">
20-
<h1 className="text-2xl font-bold text-gray-800">
21-
Join a Drawing Room
22-
</h1>
23-
<input
24-
type="text"
25-
placeholder="Enter Room ID"
26-
value={roomId}
27-
onChange={(e) => setRoomId(e.target.value)}
28-
className="border border-gray-300 rounded-lg px-4 py-2 text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
29-
/>
30-
<button
31-
onClick={handleJoin}
32-
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg font-medium transition-all"
33-
>
34-
Join Room
35-
</button>
36-
</div>
37-
) : (
38-
<Canvas roomId={roomId} />
39-
)}
40-
</main>
6+
<><Canvas /></>
417
);
428
}
439

client/src/components/Canvas.jsx

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)