Added online for contacts. Updated chat

This commit is contained in:
MaksSlyzar 2026-04-03 03:56:14 +03:00
parent 5929303523
commit b379a2a98c
4 changed files with 88 additions and 15 deletions

View File

@ -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 (
<div className={`chat-container ${isMobile ? "mobile" : ""}`}>
{confirmUnfriend && (
@ -148,7 +181,6 @@ const ChatTab = () => {
onChange={handleSearch}
/>
</div>
{searchResults.length > 0 && (
<div className="search-results-dropdown">
{searchResults.map((player) => (
@ -194,7 +226,7 @@ const ChatTab = () => {
onClick={() => selectChat(friend.id)}
>
<div
className={`status-dot ${friend.online ? "online" : "offline"}`}
className={`status-dot ${isFriendOnline(friend.username) ? "online" : "offline"}`}
></div>
<span>{friend.username}</span>
</div>

View File

@ -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({

View File

@ -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);

View File

@ -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);