Skip to content

Commit bb7ba67

Browse files
authored
Merge pull request #31 from Shreyanshi210205/shreyanshi_socket
feat: simplify App component and enhance Canvas collaboration features
2 parents dd0c68e + e67e425 commit bb7ba67

File tree

2 files changed

+65
-126
lines changed

2 files changed

+65
-126
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: 64 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ import { ColorPicker } from "./ColorPicker";
44
import { StrokeControl } from "./StrokeControl";
55
import { toast } from "sonner";
66
import { io } from "socket.io-client";
7-
import { jsPDF } from "jspdf";
87

98
export const Canvas = () => {
109
const canvasRef = useRef(null);
11-
const [activeTool, setActiveTool] = useState("pen"); // default = pen
10+
const [activeTool, setActiveTool] = useState("pen");
1211
const [activeColor, setActiveColor] = useState("#000000");
1312
const [strokeWidth, setStrokeWidth] = useState(3);
1413
const [isDrawing, setIsDrawing] = useState(false);
@@ -20,18 +19,19 @@ export const Canvas = () => {
2019

2120
// Collaboration
2221
const [roomId, setRoomId] = useState("");
23-
const [joined, setJoined] = useState(false);
22+
const [joined, setJoined] = useState(false); // false = local mode
2423
const [socket, setSocket] = useState(null);
24+
const [isModalOpen, setIsModalOpen] = useState(false); // controls modal visibility
2525

26-
// Initialize socket connection
26+
// Initialize socket connection (always available but idle until joined)
2727
useEffect(() => {
28-
const s = io("http://localhost:3000"); // replace with your backend URL
28+
const s = io("http://localhost:3000");
2929
setSocket(s);
3030

3131
s.on("connect", () => console.log("Connected to server:", s.id));
3232

33-
// Handle strokes from other users
3433
s.on("draw", ({ x, y, color, width, type, tool }) => {
34+
if (!joined) return; // only listen when in collab mode
3535
const ctx = canvasRef.current?.getContext("2d");
3636
if (!ctx) return;
3737

@@ -43,7 +43,7 @@ export const Canvas = () => {
4343
});
4444

4545
return () => s.disconnect();
46-
}, []);
46+
}, [joined]);
4747

4848
// Canvas setup
4949
useEffect(() => {
@@ -63,34 +63,24 @@ export const Canvas = () => {
6363
};
6464

6565
window.addEventListener("resize", handleResize);
66-
toast.success("Canvas ready! Start drawing!");
66+
toast.success("Canvas ready! Local mode active.");
6767
return () => window.removeEventListener("resize", handleResize);
6868
}, []);
6969

7070
// Keyboard shortcuts
7171
useEffect(() => {
7272
const handleKeyDown = (e) => {
7373
if (!isCanvasFocused) return;
74-
75-
if (e.key === "p" || e.key === "P" || e.key === "1") {
76-
handleToolChange("pen");
77-
} else if (e.key === "e" || e.key === "E" || e.key === "2") {
78-
handleToolChange("eraser");
79-
} else if (e.key === "l" || e.key === "L" || e.key === "3") {
80-
handleToolChange("line");
81-
}
74+
if (e.key === "p" || e.key === "P" || e.key === "1") handleToolChange("pen");
75+
else if (e.key === "e" || e.key === "E" || e.key === "2") handleToolChange("eraser");
76+
else if (e.key === "l" || e.key === "L" || e.key === "3") handleToolChange("line");
8277
};
83-
8478
window.addEventListener("keydown", handleKeyDown);
8579
return () => window.removeEventListener("keydown", handleKeyDown);
8680
}, [isCanvasFocused]);
8781

8882
// Drawing logic
8983
const startDrawing = (e) => {
90-
if (!joined) {
91-
toast.error("Join a room first!");
92-
return;
93-
}
9484
const canvas = canvasRef.current;
9585
const ctx = canvas?.getContext("2d");
9686
if (!ctx) return;
@@ -105,15 +95,17 @@ export const Canvas = () => {
10595
if (activeTool === "pen" || activeTool === "eraser") {
10696
ctx.beginPath();
10797
ctx.moveTo(x, y);
108-
socket.emit("draw", {
109-
roomId,
110-
x,
111-
y,
112-
type: "start",
113-
color: activeColor,
114-
width: strokeWidth,
115-
tool: activeTool,
116-
});
98+
99+
if (joined && socket)
100+
socket.emit("draw", {
101+
roomId,
102+
x,
103+
y,
104+
type: "start",
105+
color: activeColor,
106+
width: strokeWidth,
107+
tool: activeTool,
108+
});
117109
}
118110

119111
if (activeTool === "line") {
@@ -137,15 +129,16 @@ export const Canvas = () => {
137129
ctx.lineTo(x, y);
138130
ctx.stroke();
139131

140-
socket.emit("draw", {
141-
roomId,
142-
x,
143-
y,
144-
type: "move",
145-
color: activeColor,
146-
width: strokeWidth,
147-
tool: activeTool,
148-
});
132+
if (joined && socket)
133+
socket.emit("draw", {
134+
roomId,
135+
x,
136+
y,
137+
type: "move",
138+
color: activeColor,
139+
width: strokeWidth,
140+
tool: activeTool,
141+
});
149142
} else if (activeTool === "line") {
150143
ctx.putImageData(snapshot.current, 0, 0);
151144
ctx.beginPath();
@@ -175,62 +168,20 @@ export const Canvas = () => {
175168
if (!roomId.trim() || !socket) return;
176169
socket.emit("join-room", roomId.trim());
177170
setJoined(true);
178-
toast.success(`Joined room: ${roomId}`);
171+
setIsModalOpen(false);
172+
toast.success(`Collaborative mode active - joined room: ${roomId}`);
179173
};
180174

181175
const handleToolChange = (tool) => {
182176
setActiveTool(tool);
183177
toast.info(`${tool.charAt(0).toUpperCase() + tool.slice(1)} tool selected`);
184178
};
185179

186-
const handleExport = (format) => {
187-
const canvas = canvasRef.current;
188-
if (!canvas) return;
189-
190-
try {
191-
if (format === "png") {
192-
canvas.toBlob((blob) => {
193-
const url = URL.createObjectURL(blob);
194-
const link = document.createElement("a");
195-
link.href = url;
196-
link.download = `canvas-${Date.now()}.png`;
197-
link.click();
198-
URL.revokeObjectURL(url);
199-
toast.success("Canvas exported as PNG!");
200-
});
201-
} else if (format === "svg") {
202-
// Create SVG from canvas
203-
const svgString = `
204-
<svg xmlns="http://www.w3.org/2000/svg" width="${canvas.width}" height="${canvas.height}">
205-
<foreignObject width="100%" height="100%">
206-
<img xmlns="http://www.w3.org/1999/xhtml" src="${canvas.toDataURL()}" width="${canvas.width}" height="${canvas.height}"/>
207-
</foreignObject>
208-
</svg>
209-
`;
210-
const blob = new Blob([svgString], { type: "image/svg+xml" });
211-
const url = URL.createObjectURL(blob);
212-
const link = document.createElement("a");
213-
link.href = url;
214-
link.download = `canvas-${Date.now()}.svg`;
215-
link.click();
216-
URL.revokeObjectURL(url);
217-
toast.success("Canvas exported as SVG!");
218-
} else if (format === "pdf") {
219-
// Export as PDF using jsPDF
220-
const imgData = canvas.toDataURL("image/png");
221-
const pdf = new jsPDF({
222-
orientation: canvas.width > canvas.height ? "landscape" : "portrait",
223-
unit: "px",
224-
format: [canvas.width, canvas.height],
225-
});
226-
227-
pdf.addImage(imgData, "PNG", 0, 0, canvas.width, canvas.height);
228-
pdf.save(`canvas-${Date.now()}.pdf`);
229-
toast.success("Canvas exported as PDF!");
230-
}
231-
} catch (error) {
232-
toast.error("Failed to export canvas!");
233-
console.error("Export error:", error);
180+
const handleExitRoom = () => {
181+
if (socket) {
182+
socket.emit("leave-room", roomId);
183+
setJoined(false);
184+
toast.success(`Left room: ${roomId}`);
234185
}
235186
};
236187

@@ -240,8 +191,22 @@ export const Canvas = () => {
240191
activeTool={activeTool}
241192
onToolChange={handleToolChange}
242193
onClear={handleClear}
243-
onExport={handleExport}
244194
/>
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+
245210
<ColorPicker activeColor={activeColor} onColorChange={setActiveColor} />
246211
<StrokeControl
247212
strokeWidth={strokeWidth}
@@ -260,7 +225,7 @@ export const Canvas = () => {
260225
className="cursor-crosshair focus:outline-2 focus:outline-primary"
261226
/>
262227

263-
{!joined && (
228+
{isModalOpen && (
264229
<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">
265230
<h2 className="text-xl font-semibold">Join a Room</h2>
266231
<input
@@ -276,13 +241,21 @@ export const Canvas = () => {
276241
>
277242
Join Room
278243
</button>
244+
<button
245+
onClick={() => setIsModalOpen(false)}
246+
className="text-sm text-gray-600 mt-2 hover:underline"
247+
>
248+
Cancel
249+
</button>
279250
</div>
280251
)}
281252

282253
<div className="fixed bottom-6 left-1/2 -translate-x-1/2 pointer-events-none">
283254
<div className="bg-toolbar/95 border border-toolbar-border rounded-xl shadow-lg backdrop-blur-sm px-6 py-3">
284255
<p className="text-sm text-foreground font-medium">
285-
Welcome to CollabCanvas — Select a tool and start drawing!
256+
{joined
257+
? `Connected to Room: ${roomId}`
258+
: "Local mode - your drawings are not shared."}
286259
</p>
287260
</div>
288261
</div>

0 commit comments

Comments
 (0)