diff --git a/client/src/views/GameInterface/tabs/ChatTab.jsx b/client/src/views/GameInterface/tabs/ChatTab.jsx index fcbe52e..bfdeaec 100644 --- a/client/src/views/GameInterface/tabs/ChatTab.jsx +++ b/client/src/views/GameInterface/tabs/ChatTab.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useRef } from "react"; import { useSocket } from "../../../hooks/useSocket"; import "./styles/ChatTab.css"; +import PlayerManager from "../../../services/PlayerManager"; const ChatTab = () => { const { socket } = useSocket(); @@ -13,8 +14,16 @@ const ChatTab = () => { const [showSidebar, setShowSidebar] = useState(true); const [isMobile, setIsMobile] = useState(window.innerWidth <= 768); const [confirmUnfriend, setConfirmUnfriend] = useState(null); + const [onlineList, setOnlineList] = useState( + PlayerManager.onlinePlayers || [], + ); const messagesEndRef = useRef(null); + const activeChatRef = useRef(activeChat); + + useEffect(() => { + activeChatRef.current = activeChat; + }, [activeChat]); useEffect(() => { const handleResize = () => { @@ -30,17 +39,34 @@ const ChatTab = () => { if (!socket) return; socket.emit("friend:get_list"); + if (activeChat === "global") { socket.emit("chat:get_global_history"); + } else { + socket.emit("chat:get_private_history", { friendId: activeChat }); } + }, [socket, activeChat]); + + useEffect(() => { + if (!socket) return; + + const interval = setInterval(() => { + setOnlineList([...PlayerManager.onlinePlayers]); + }, 3000); const handleNewMessage = (msg) => { - if ( - (activeChat === "global" && msg.type === "global") || - (activeChat !== "global" && - (msg.senderId === activeChat || msg.receiverId === activeChat)) - ) { - setMessages((prev) => [...prev, msg]); + const currentActive = activeChatRef.current; + const isGlobalMatch = currentActive === "global" && msg.type === "global"; + const isPrivateMatch = + currentActive !== "global" && + msg.type === "private" && + (msg.senderId === currentActive || msg.receiverId === currentActive); + + if (isGlobalMatch || isPrivateMatch) { + setMessages((prev) => { + if (prev.some((m) => m.id === msg.id)) return prev; + return [...prev, msg]; + }); } }; @@ -50,16 +76,19 @@ const ChatTab = () => { socket.on("chat:new_message", handleNewMessage); socket.on("chat:global_history", handleHistory); + socket.on("chat:private_history", handleHistory); socket.on("player:search_results", handleSearchResults); socket.on("friend:list", handleFriendList); return () => { + clearInterval(interval); socket.off("chat:new_message", handleNewMessage); socket.off("chat:global_history", handleHistory); + socket.off("chat:private_history", handleHistory); socket.off("player:search_results", handleSearchResults); socket.off("friend:list", handleFriendList); }; - }, [socket, activeChat]); + }, [socket]); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); @@ -110,6 +139,10 @@ const ChatTab = () => { return friend ? friend.username : "UNKNOWN_PILOT"; }; + const isFriendOnline = (username) => { + return onlineList.includes(username); + }; + return (
{confirmUnfriend && ( @@ -148,7 +181,6 @@ const ChatTab = () => { onChange={handleSearch} />
- {searchResults.length > 0 && (
{searchResults.map((player) => ( @@ -194,7 +226,7 @@ const ChatTab = () => { onClick={() => selectChat(friend.id)} >
{friend.username}
diff --git a/game-server/src/game/ChatManager.js b/game-server/src/game/ChatManager.js index 0e79a4f..7e168e9 100644 --- a/game-server/src/game/ChatManager.js +++ b/game-server/src/game/ChatManager.js @@ -49,7 +49,31 @@ class ChatManager { throw error; } } - + async getPrivateHistory(myId, friendId, limit = 50) { + try { + return await Message.findAll({ + where: { + type: "private", + [Op.or]: [ + { senderId: myId, receiverId: friendId }, + { senderId: friendId, receiverId: myId }, + ], + }, + limit, + order: [["createdAt", "ASC"]], + include: [ + { + model: Player, + as: "sender", + attributes: ["username"], + }, + ], + }); + } catch (error) { + console.error("Private History Error:", error); + return []; + } + } async searchPlayers(query, excludeId) { try { return await Player.findAll({ diff --git a/game-server/src/sockets/handlers/chatHandler.js b/game-server/src/sockets/handlers/chatHandler.js index 1cdc2b4..6ceffda 100644 --- a/game-server/src/sockets/handlers/chatHandler.js +++ b/game-server/src/sockets/handlers/chatHandler.js @@ -14,7 +14,6 @@ module.exports = (io, socket) => { socket.emit("chat:global_history", formattedHistory); }); socket.on("chat:get_global_history", async () => { - console.log(`Player ${socket.player?.id} requested chat history`); const history = await chatManager.getGlobalHistory(); const formattedHistory = history.map((m) => ({ @@ -28,10 +27,12 @@ module.exports = (io, socket) => { socket.emit("chat:global_history", formattedHistory); }); + socket.on("chat:send_message", async (payload) => { try { const senderId = socket.user?.id; if (!senderId) return; + const messageData = await chatManager.saveMessage({ content: payload.content, type: payload.type || "global", @@ -42,20 +43,36 @@ module.exports = (io, socket) => { if (messageData.type === "global") { io.emit("chat:new_message", messageData); } else { - console.log(payload.receiverId); socket .to(`user_${payload.receiverId}`) .emit("chat:new_message", messageData); socket.emit("chat:new_message", messageData); } } catch (err) { - console.log(err); + console.error(err); socket.emit("error", { message: "CHAT_SEND_ERROR" }); } }); + socket.on("chat:get_private_history", async ({ friendId }) => { + const myId = socket.user?.id; + + if (!myId || !friendId) return; + + const history = await chatManager.getPrivateHistory(myId, friendId); + const formattedHistory = history.map((m) => ({ + id: m.id, + content: m.content, + senderName: m.sender?.username || "Unknown", + senderId: m.senderId, + receiverId: m.receiverId, + createdAt: m.createdAt, + type: m.type, + })); + socket.emit("chat:private_history", formattedHistory); + }); socket.on("player:search", async ({ query }) => { - const senderId = socket.player?.id; + const senderId = socket.user?.id; if (!query || !senderId) return; const results = await chatManager.searchPlayers(query, senderId); diff --git a/game-server/src/sockets/handlers/connectionHandler.js b/game-server/src/sockets/handlers/connectionHandler.js index e103c08..a129d95 100644 --- a/game-server/src/sockets/handlers/connectionHandler.js +++ b/game-server/src/sockets/handlers/connectionHandler.js @@ -63,7 +63,7 @@ module.exports = async (io, socket) => { }); socket.broadcast.emit("player:joined", { username: playerRaw.username }); - + socket.join(`user_${socket.user.id}`); socket.on("player:get_dashboard", async () => { try { const p = await Player.findByPk(userId);