From f47b952b38161c3f79d02be4c345de1f38989ae0 Mon Sep 17 00:00:00 2001 From: Justin Xiao Date: Mon, 24 Jul 2023 00:13:35 +0800 Subject: [PATCH] fix: remove ajax method in dev env --- frontend/src/app/home/page.tsx | 9 ++- frontend/src/app/provide.tsx | 8 +-- frontend/src/components/ChatroomTitle.tsx | 7 +- frontend/src/pages/api/socket/broadcast.ts | 20 ------ .../src/pages/api/socket/private_message.ts | 33 ---------- frontend/src/pages/api/socket/socketio.ts | 64 +++++++++++++++++-- frontend/src/store/socket.ts | 36 +++-------- 7 files changed, 81 insertions(+), 96 deletions(-) delete mode 100644 frontend/src/pages/api/socket/broadcast.ts delete mode 100644 frontend/src/pages/api/socket/private_message.ts diff --git a/frontend/src/app/home/page.tsx b/frontend/src/app/home/page.tsx index e607ba9..0a39eb0 100644 --- a/frontend/src/app/home/page.tsx +++ b/frontend/src/app/home/page.tsx @@ -96,8 +96,7 @@ export default function Home() { useEffect(() => { connect(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [connect]); useEffect(() => { if (!socket) return; @@ -160,7 +159,11 @@ export default function Home() { > {!message.me && (
- + {onlineUsers[message.from] && onlineUsers[message.from].length > 5 ? `${onlineUsers[message.from].slice(0, 1)}` diff --git a/frontend/src/app/provide.tsx b/frontend/src/app/provide.tsx index 84d73a3..1c827f1 100644 --- a/frontend/src/app/provide.tsx +++ b/frontend/src/app/provide.tsx @@ -8,15 +8,15 @@ type Prop = { children: React.ReactNode; }; export function AppProvider({ children }: Prop) { - const [disconnect] = useSocketStore(({ disconnect }) => [disconnect]); + const disconnect = useSocketStore((state) => state.disconnect); useEffect(() => { + window.addEventListener("beforeunload", disconnect); return () => { - console.log("disconnect"); + window.removeEventListener("beforeunload", disconnect); disconnect(); }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [disconnect]); return ( diff --git a/frontend/src/components/ChatroomTitle.tsx b/frontend/src/components/ChatroomTitle.tsx index e152aa0..5185a85 100644 --- a/frontend/src/components/ChatroomTitle.tsx +++ b/frontend/src/components/ChatroomTitle.tsx @@ -29,7 +29,6 @@ import { } from "@tabler/icons-react"; import { SetStateAction, Dispatch, FC, useEffect, useState } from "react"; import useSocketStore from "@/store/socket"; -import { environment } from "@/config"; import Avatar from "./Avatar"; type Props = { @@ -218,11 +217,7 @@ const ChatroomTitle: FC = ({ targetSocketId, setTargetSocketId }) => { - - {environment === "development" - ? "Not available" // in development mode, hide online user list because it's a server side feature - : "Online user"} - + Online user {socket?.connected && Object.keys(onlineUsers) .filter((socketId) => socketId !== socket?.id) diff --git a/frontend/src/pages/api/socket/broadcast.ts b/frontend/src/pages/api/socket/broadcast.ts deleted file mode 100644 index 64177b4..0000000 --- a/frontend/src/pages/api/socket/broadcast.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NextApiResponseServerIO, SocketBroadcastMessage } from "@/types/next"; -import { NextApiRequest } from "next"; - -const broadcast = (req: NextApiRequest, res: NextApiResponseServerIO) => { - if (req.method === "POST") { - // get message - const { from: sourceSocketId, timestamp, message } = req.body as SocketBroadcastMessage; - console.log("PRODUCTION SERVER: Broadcast ", message) - // dispatch to channel "message" - res?.socket?.server?.io?.emit("broadcast", { - from: sourceSocketId, - message, - timestamp, - } as SocketBroadcastMessage); - // return message - res.status(201).json(message); - } -}; - -export default broadcast; diff --git a/frontend/src/pages/api/socket/private_message.ts b/frontend/src/pages/api/socket/private_message.ts deleted file mode 100644 index 2f8ebee..0000000 --- a/frontend/src/pages/api/socket/private_message.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { NextApiResponseServerIO, SocketPrivateMessage } from "@/types/next"; -import { NextApiRequest } from "next"; - -const private_message = (req: NextApiRequest, res: NextApiResponseServerIO) => { - if (req.method === "POST") { - // get message - const { - from: sourceSocketId, - to: targetSocketId, - timestamp, - message, - } = req.body as SocketPrivateMessage; - - // dispatch to channel "message" - res?.socket?.server?.io?.to(targetSocketId).emit("private_message", { - from: sourceSocketId, - to: targetSocketId, - message, - timestamp, - } as SocketPrivateMessage); - res?.socket?.server?.io?.to(sourceSocketId).emit("private_message", { - from: sourceSocketId, - to: targetSocketId, - message, - timestamp, - } as SocketPrivateMessage); - - // return message - res.status(201).json(message); - } -}; - -export default private_message; diff --git a/frontend/src/pages/api/socket/socketio.ts b/frontend/src/pages/api/socket/socketio.ts index 091991c..78a22d3 100644 --- a/frontend/src/pages/api/socket/socketio.ts +++ b/frontend/src/pages/api/socket/socketio.ts @@ -9,6 +9,10 @@ export const config = { }, }; +const onlineUsers = new Map(); +let isEmitting = false; +let sendOnlineUsers: NodeJS.Timeout; + const socketio = async (req: NextApiRequest, res: NextApiResponseServerIO) => { if (!res.socket.server.io) { console.log("MOCK SERVER: First connect on socket.io"); @@ -18,10 +22,62 @@ const socketio = async (req: NextApiRequest, res: NextApiResponseServerIO) => { path: "/api/socket/socketio", addTrailingSlash: false, }); - io.on("connect", (socket) => { - console.log("MOCK SERVER: SOCKET CONNECTED!", socket.id); - }).on("disconnect", () => { - console.log("MOCK SERVER: SOCKET DISCONNECTED!"); + io.on("connection", (socket) => { + console.log("MOCK SERVER: user connected, online user count:", onlineUsers.size); + + socket.on("join", (data) => { + const { socketId, name = socketId } = data; + onlineUsers.set(socketId, name); + // console.log( + // 'MOCK SERVER: user joined, online user count:', + // 'socketId: ', + // socketId, + // 'name: ', + // name, + // ); + }); + + socket.on("broadcast", (broadcast, callback) => { + console.log("MOCK SERVER: Broadcast ", broadcast); + io.emit("broadcast", broadcast); + if (callback) { + callback({ + ok: true, + }); + } + }); + + socket.on("private_message", (message, callback) => { + console.log("MOCK SERVER: private_message", message); + const { from: sourceSocketId, to: targetSocketId } = message; + io.to(targetSocketId).emit("private_message", message); + io.to(sourceSocketId).emit("private_message", message); + if (callback) { + callback({ + ok: true, + }); + } + }); + + socket.on("disconnect", () => { + onlineUsers.delete(socket.id); + console.log( + "MOCK SERVER: user disconnected, online user count:", + onlineUsers.size + ); + if (isEmitting && onlineUsers.size === 0) { + clearInterval(sendOnlineUsers); + isEmitting = false; + } + }); + + if (!isEmitting) { + sendOnlineUsers = setInterval( + () => io.emit("online_user", Object.fromEntries(onlineUsers)), + 5000 + ); + isEmitting = true; + } }); // append SocketIO server to Next.js socket server response res.socket.server.io = io; diff --git a/frontend/src/store/socket.ts b/frontend/src/store/socket.ts index 41ce245..b9863c4 100644 --- a/frontend/src/store/socket.ts +++ b/frontend/src/store/socket.ts @@ -12,8 +12,8 @@ type EmitModeDataTypes = { type Store = { socket: null | Socket; - emitMode: "broadcast" | "private_message"; - setEmitMode: (mode: "broadcast" | "private_message") => void; + emitMode: keyof EmitModeDataTypes; + setEmitMode: (mode: keyof EmitModeDataTypes) => void; emit: (event: T, data: EmitModeDataTypes[T]) => void; connect: () => void; disconnect: () => void; @@ -33,30 +33,14 @@ const useSocketStore = create((set, get) => { * @param data - The data to send along with the event. */ emit: (event, data) => { - // console.log("emit", event, data); - // Check if environment is development - if (environment === "development") { - // Send a POST request to the /api/socket/${event} endpoint with the data - fetch(`/api/socket/${event}`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }).catch((error) => { - // Display an error message if there was an error sending the request - if (error instanceof Error) toast.error(error?.message); - }); - } else { - const { socket } = get(); - if (!socket) return toast.error("Socket not connected"); - // This callback response needs to define on server at first. - // Emit the event with the data and handle the response - socket.emit(event, data, (response: { ok: boolean }) => { - // Display an error message if response.ok is false - if (!response.ok) toast.error("Something went wrong"); - }); - } + const { socket } = get(); + if (!socket) return toast.error("Socket not connected"); + // This callback response needs to define on server at first. + // Emit the event with the data and handle the response + socket.emit(event, data, (response: { ok: boolean }) => { + // Display an error message if response.ok is false + if (!response.ok) toast.error("Something went wrong"); + }); }, /** * Connects to the socket server.