From 7d2be83dbadc3941f893d112f3f15abbbaf3a232 Mon Sep 17 00:00:00 2001 From: Deveshi Dwivedi Date: Sat, 22 Jun 2024 18:43:42 +0530 Subject: [PATCH] refactored code --- modules/room/components/Canvas.tsx | 4 +- modules/room/context/Room.context.tsx | 24 ++- modules/room/helpers/Canvas.helpers.ts | 11 +- modules/room/hooks/Canvas.hooks.ts | 202 ------------------------- modules/room/hooks/useDraw.ts | 105 +++++++++++++ modules/room/hooks/useSocketDraw.ts | 47 ++++++ server/index.ts | 2 + 7 files changed, 178 insertions(+), 217 deletions(-) delete mode 100644 modules/room/hooks/Canvas.hooks.ts create mode 100644 modules/room/hooks/useDraw.ts create mode 100644 modules/room/hooks/useSocketDraw.ts diff --git a/modules/room/components/Canvas.tsx b/modules/room/components/Canvas.tsx index 4ae8324..f8a2c77 100644 --- a/modules/room/components/Canvas.tsx +++ b/modules/room/components/Canvas.tsx @@ -8,7 +8,9 @@ import { useRef, useState, useEffect } from "react"; import { useKeyPressEvent } from "react-use"; -import { useDraw, useSocketDraw } from "../hooks/Canvas.hooks"; +import { useDraw } from "../hooks/useDraw"; + +import { useSocketDraw } from "../hooks/useSocketDraw"; import { socket } from "@/common/lib/socket"; diff --git a/modules/room/context/Room.context.tsx b/modules/room/context/Room.context.tsx index 96ed492..420f7d9 100644 --- a/modules/room/context/Room.context.tsx +++ b/modules/room/context/Room.context.tsx @@ -21,23 +21,31 @@ const RoomContextProvider = ({ children }: { children: ReactChild }) => { // socket events for user join/leave and clean up listeners useEffect(()=> { + + socket.on("room",(room, usersToParse)=> { + const users= new Map< string, Move[]>(JSON.parse(usersToParse)); + + setRoom((prev)=>({ + ...prev, + users, + movesWithoutUser: room.drawed, + })); + }); + socket.on("new_user", (newUser)=> { - setUsers((prevUsers)=> ({...prevUsers, [newUser]: [] })); - }) + handleAddUser(newUser); + }); socket.on("user_disconnected", (userId)=> { - setUsers((prevUsers)=> { - const newUsers= {...prevUsers}; - delete newUsers[userId]; - return newUsers; - }); + handleRemoveUser(userId); }); return () => { + socket.off("room"); socket.off("new_user"); socket.off("user_disconnected"); }; - }, [setUsers, usersIds]); + }, [handleAddUser, handleRemoveUser, setRoom]); //motion values to context consumers return ( diff --git a/modules/room/helpers/Canvas.helpers.ts b/modules/room/helpers/Canvas.helpers.ts index a26d141..5c6fb92 100644 --- a/modules/room/helpers/Canvas.helpers.ts +++ b/modules/room/helpers/Canvas.helpers.ts @@ -20,21 +20,20 @@ export const handleMove = ( export const drawAllMoves = ( ctx: CanvasRenderingContext2D, - movesWithoutUser: Move[], - savedMoves: Move[], - users: {[key: string]: Move[]} + room: ClientRoom ) => { + const {users, movesWithoutUser, myMoves} = room; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); movesWithoutUser.forEach((move)=>{ handleMove(move,ctx); }); - Object.values(users).forEach((user)=>{ - user.forEach((move)=> handleMove(move,ctx)); + users.forEach((userMoves)=>{ + userMoves.forEach((move)=> handleMove(move,ctx)); }); - savedMoves.forEach((move)=> { + myMoves.forEach((move)=> { handleMove(move,ctx); }); diff --git a/modules/room/hooks/Canvas.hooks.ts b/modules/room/hooks/Canvas.hooks.ts deleted file mode 100644 index 6908a97..0000000 --- a/modules/room/hooks/Canvas.hooks.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { use, useCallback, useEffect, useState } from "react"; -import { socket } from "@/common/lib/socket"; -import { useOptions } from "@/common/recoil/options"; -import { drawAllMoves, handleMove } from "../helpers/Canvas.helpers"; -import usersAtom, { useUsers } from "@/common/recoil/users"; -import { useBoardPosition } from "./useBoardPosition"; -import { getPos } from "@/common/lib/getPos"; -import { useSetRecoilState } from "recoil"; - -const movesWithoutUser: Move[] = []; -const savedMoves: Move[] = []; -let tempMoves: [number, number][] = []; - -export const useDraw = ( - ctx: CanvasRenderingContext2D | undefined, - blocked: boolean, - handleEnd: () => void -) => { - const users = useUsers(); - const options = useOptions(); - const [drawing, setDrawing] = useState(false); - - const boardPosition = useBoardPosition(); - const movedX = boardPosition.x; - const movedY = boardPosition.y; - - useEffect(() => { - if (ctx) { - ctx.lineJoin = "round"; - ctx.lineCap = "round"; - ctx.lineWidth = options.lineWidth; - ctx.strokeStyle = options.lineColor; - } - }); - - const handleUndo = useCallback(() => { - if (ctx) { - savedMoves.pop(); - - socket.emit("undo"); - - drawAllMoves(ctx, movesWithoutUser, savedMoves, users); - handleEnd(); - } - }, [ctx, handleEnd, users]); - - useEffect(() => { - const handleUndoKeyboard = (e: KeyboardEvent) => { - if (e.key === 'z' && e.ctrlKey) { - handleUndo(); - } - }; - - document.addEventListener('keydown', handleUndoKeyboard); - return () => { - document.removeEventListener('keydown', handleUndoKeyboard); - }; - }, [handleUndo]); - - const handleStartDrawing = (x: number, y: number) => { - if (!ctx || blocked) return; - - setDrawing(true); - - ctx.beginPath(); - ctx.lineTo(getPos(x, movedX), getPos(y, movedY)); - ctx.stroke(); - - tempMoves = [[getPos(x, movedX), getPos(y, movedY)]]; - }; - - const handleDraw = (x: number, y: number) => { - if (!ctx || !drawing || blocked) return; - - ctx.lineTo(getPos(x, movedX), getPos(y, movedY)); - ctx.stroke(); - tempMoves.push([getPos(x, movedX), getPos(y, movedY)]); - }; - - const handleEndDrawing = () => { - if (!ctx || blocked) return; - ctx.closePath(); - setDrawing(false); - - const move: Move = { - path: tempMoves, - options - }; - - savedMoves.push(move); - tempMoves = []; - - socket.emit("draw", move); - - drawAllMoves(ctx, movesWithoutUser, savedMoves, users); - - handleEnd(); - }; - - - - return { - handleEndDrawing, - handleDraw, - handleStartDrawing, - handleUndo, - drawing, - } -}; - -// Hook to listen for user draw & undo events -export const useSocketDraw = ( - ctx: CanvasRenderingContext2D | undefined, - drawing: boolean, - handleEnd: () => void -) => { - const setUsers = useSetRecoilState(usersAtom); - - useEffect(()=> { - if(ctx) socket.emit("joined_room") - },[ctx]); - - useEffect(() => { - socket.on("room", (room, usersToParse) => { - if(!ctx) return; - - const users= new Map(JSON.parse(usersToParse)); - - room.drawed.forEach((move) => { - handleMove(move, ctx); - movesWithoutUser.push(move); - }); - - users.forEach((userMoves, userId) => { - userMoves.forEach((move)=>handleMove(move, ctx) ); - setUsers((prevUsers)=> ({...prevUsers, [userId]: userMoves})); - }); - handleEnd(); - - }); - - - - return () => { - socket.off("room"); - } - }, [ctx, handleEnd, setUsers]); - - useEffect(() => { - let movesToDrawLater: Move | undefined; - let userIdLater = ""; - socket.on("user_draw", (move, userId) => { - if (ctx && !drawing) { - handleMove(move, ctx); - - setUsers((prevUsers) => { - const newUsers = { ...prevUsers }; - if (newUsers[userId]) newUsers[userId] = [...newUsers[userId], move]; - return newUsers; - }); - } else { - movesToDrawLater = move; - userIdLater = userId; - } - }); - - return () => { - socket.off("user_draw"); - - if (movesToDrawLater && userIdLater && ctx) { - handleMove(movesToDrawLater, ctx); - handleEnd(); - setUsers((prevUsers) => { - const newUsers = { ...prevUsers }; - newUsers[userIdLater] = [ - ...newUsers[userIdLater], movesToDrawLater as Move]; - return newUsers; - }) - } - }; - }, [ctx, handleEnd, setUsers, drawing]); - - useEffect(() => { - socket.on("user_undo", (userId) => { - setUsers((prevUsers) => { - const newUsers = { ...prevUsers }; - if (Array.isArray(newUsers[userId])) { - newUsers[userId] = newUsers[userId].slice(0, -1); - } - if (ctx) { - drawAllMoves(ctx,movesWithoutUser, savedMoves, newUsers); - handleEnd(); - } - return newUsers; - }); - }); - return () => { - socket.off("user_undo"); - }; - }, [ctx, handleEnd, setUsers]); - -}; diff --git a/modules/room/hooks/useDraw.ts b/modules/room/hooks/useDraw.ts new file mode 100644 index 0000000..3131a73 --- /dev/null +++ b/modules/room/hooks/useDraw.ts @@ -0,0 +1,105 @@ +import { useOptions } from "@/common/recoil/options"; +import { useUsers } from "@/common/recoil/users"; +import { useState, useEffect, useCallback} from "react"; +import { useBoardPosition } from "./useBoardPosition"; +import { socket } from "@/common/lib/socket"; +import { drawAllMoves } from "../helpers/Canvas.helpers"; +import { getPos } from "@/common/lib/getPos"; +import { useMyMoves } from "@/common/recoil/room"; + + + +let tempMoves: [number, number][] = []; + +export const useDraw = ( + ctx: CanvasRenderingContext2D | undefined, + blocked: boolean, +) => { + const {handleRemoveMyMove, handleAddMyMove} = useMyMoves(); + + const [drawing, setDrawing] = useState(false); + + const boardPosition = useBoardPosition(); + const movedX = boardPosition.x; + const movedY = boardPosition.y; + + const options = useOptions(); + + useEffect(() => { + if (ctx) { + ctx.lineJoin = "round"; + ctx.lineCap = "round"; + ctx.lineWidth = options.lineWidth; + ctx.strokeStyle = options.lineColor; + } + }); + + const handleUndo = useCallback(() => { + if (ctx) { + handleRemoveMyMove(); + socket.emit("undo"); + + } + }, [ctx, handleRemoveMyMove]); + + useEffect(() => { + const handleUndoKeyboard = (e: KeyboardEvent) => { + if (e.key === 'z' && e.ctrlKey) { + handleUndo(); + } + }; + + document.addEventListener('keydown', handleUndoKeyboard); + return () => { + document.removeEventListener('keydown', handleUndoKeyboard); + }; + }, [handleUndo]); + + const handleStartDrawing = (x: number, y: number) => { + if (!ctx || blocked) return; + + setDrawing(true); + + ctx.beginPath(); + ctx.lineTo(getPos(x, movedX), getPos(y, movedY)); + ctx.stroke(); + + tempMoves = [[getPos(x, movedX), getPos(y, movedY)]]; + }; + + const handleDraw = (x: number, y: number) => { + if (!ctx || !drawing || blocked) return; + + ctx.lineTo(getPos(x, movedX), getPos(y, movedY)); + ctx.stroke(); + tempMoves.push([getPos(x, movedX), getPos(y, movedY)]); + }; + + const handleEndDrawing = () => { + if (!ctx || blocked) return; + ctx.closePath(); + setDrawing(false); + + const move: Move = { + path: tempMoves, + options + }; + + handleAddMyMove(move); + tempMoves = []; + + socket.emit("draw", move); + + + }; + + + + return { + handleEndDrawing, + handleDraw, + handleStartDrawing, + handleUndo, + drawing, + } +}; \ No newline at end of file diff --git a/modules/room/hooks/useSocketDraw.ts b/modules/room/hooks/useSocketDraw.ts new file mode 100644 index 0000000..a759ece --- /dev/null +++ b/modules/room/hooks/useSocketDraw.ts @@ -0,0 +1,47 @@ +import { useEffect } from "react"; +import { socket } from "@/common/lib/socket"; + +import { handleMove } from "../helpers/Canvas.helpers"; + +import { useSetUsers } from "@/common/recoil/room"; + + +// Hook to listen for user draw & undo events +export const useSocketDraw = ( + ctx: CanvasRenderingContext2D | undefined, + drawing: boolean, +) => { + const {handleAddMoveToUser, handleRemoveMoveFromUser}= useSetUsers(); + + useEffect(() => { + let movesToDrawLater: Move | undefined; + let userIdLater = ""; + socket.on("user_draw", (move, userId) => { + if (ctx && !drawing) { + handleAddMoveToUser(userId, move); + } else { + movesToDrawLater = move; + userIdLater = userId; + } + }); + + return () => { + socket.off("user_draw"); + + if (movesToDrawLater && userIdLater && ctx) { + handleMove(movesToDrawLater, ctx); + handleAddMoveToUser(userIdLater, movesToDrawLater); + } + }; + }, [ctx, drawing, handleAddMoveToUser]); + + useEffect(() => { + socket.on("user_undo", (userId) => { + handleRemoveMoveFromUser(userId); + }); + return () => { + socket.off("user_undo"); + }; + }, [ctx, handleRemoveMoveFromUser]); + +}; diff --git a/server/index.ts b/server/index.ts index e283752..f2ec2f1 100644 --- a/server/index.ts +++ b/server/index.ts @@ -38,6 +38,8 @@ nextApp.prepare().then(async () => { const leaveRoom = (roomId: string, socketId: string) => { const room= rooms.get(roomId); + if(!room) return; + const userMoves= room?.users.get(socketId)!; room?.drawed.push(...userMoves); room?.users.delete(socketId);