Game-Server/client/src/views/GameInterface/tabs/ChatTab.jsx

234 lines
7.2 KiB
JavaScript

import React, { useState, useEffect, useRef } from "react";
import { useSocket } from "../../../hooks/useSocket";
import "./styles/ChatTab.css";
const ChatTab = () => {
const { socket } = useSocket();
const [activeChat, setActiveChat] = useState("global");
const [searchQuery, setSearchQuery] = useState("");
const [searchResults, setSearchResults] = useState([]);
const [messages, setMessages] = useState([]);
const [friends, setFriends] = useState([]);
const [inputValue, setInputValue] = useState("");
const [showSidebar, setShowSidebar] = useState(true);
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
const messagesEndRef = useRef(null);
useEffect(() => {
const handleResize = () => {
const mobile = window.innerWidth <= 768;
setIsMobile(mobile);
if (!mobile) setShowSidebar(true);
};
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
useEffect(() => {
if (!socket) return;
socket.emit("friend:get_list");
if (activeChat === "global") {
socket.emit("chat:get_global_history");
}
const handleNewMessage = (msg) => {
if (
(activeChat === "global" && msg.type === "global") ||
(activeChat !== "global" &&
(msg.senderId === activeChat || msg.receiverId === activeChat))
) {
setMessages((prev) => [...prev, msg]);
}
};
const handleHistory = (history) => setMessages(history);
const handleSearchResults = (results) => setSearchResults(results);
const handleFriendList = (list) => setFriends(list);
socket.on("chat:new_message", handleNewMessage);
socket.on("chat:global_history", handleHistory);
socket.on("player:search_results", handleSearchResults);
socket.on("friend:list", handleFriendList);
return () => {
socket.off("chat:new_message", handleNewMessage);
socket.off("chat:global_history", handleHistory);
socket.off("player:search_results", handleSearchResults);
socket.off("friend:list", handleFriendList);
};
}, [socket, activeChat]);
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
const handleSearch = (e) => {
const query = e.target.value;
setSearchQuery(query);
if (query.length > 1) {
socket.emit("player:search", { query });
} else {
setSearchResults([]);
}
};
const addFriend = (player) => {
socket.emit("friend:add", { friendId: player.id });
setSearchQuery("");
setSearchResults([]);
};
const sendMessage = () => {
if (!inputValue.trim() || !socket) return;
socket.emit("chat:send_message", {
content: inputValue,
type: activeChat === "global" ? "global" : "private",
receiverId: activeChat === "global" ? null : activeChat,
});
setInputValue("");
console.log(activeChat === "global" ? null : activeChat);
};
const selectChat = (id) => {
setActiveChat(id);
if (isMobile) setShowSidebar(false);
};
const getChatName = () => {
if (activeChat === "global") return "GLOBAL_SYSTEM_CHAT";
const friend = friends.find((f) => f.id === activeChat);
return friend ? friend.username : "UNKNOWN_PILOT";
};
return (
<div className={`chat-container ${isMobile ? "mobile" : ""}`}>
<aside
className={`chat-sidebar ${isMobile && !showSidebar ? "hidden" : ""}`}
>
<div className="search-section">
<div className="card-tag">USER_SEARCH</div>
<div className="search-input-wrapper">
<input
type="text"
placeholder="SEARCH_PILOTS..."
value={searchQuery}
onChange={handleSearch}
/>
</div>
{searchResults.length > 0 && (
<div className="search-results-dropdown">
{searchResults.map((player) => (
<div
key={player.id}
className="search-result-item"
onClick={() => addFriend(player)}
>
<span>
{player.username} (LVL {player.level})
</span>
<i className="fas fa-plus"></i>
</div>
))}
</div>
)}
</div>
<div className="chats-list">
<div
className={`chat-item ${activeChat === "global" ? "active" : ""}`}
onClick={() => selectChat("global")}
>
<i className="fas fa-globe"></i>
<span>GLOBAL_CHANNEL</span>
</div>
<div className="friends-section-label">
<span className="label-text">CONTACTS</span>
<span className="label-line"></span>
</div>
<div className="friends-list">
{friends.map((friend) => (
<div
key={friend.id}
className={`chat-item ${activeChat === friend.id ? "active" : ""}`}
onClick={() => selectChat(friend.id)}
>
<div
className={`status-dot ${friend.online ? "online" : "offline"}`}
></div>
<span>{friend.username}</span>
</div>
))}
</div>
</div>
</aside>
<main className={`chat-main ${isMobile && showSidebar ? "hidden" : ""}`}>
<div className="chat-header">
{isMobile && (
<button className="back-btn" onClick={() => setShowSidebar(true)}>
<i className="fas fa-chevron-left"></i>
</button>
)}
<div className="active-chat-info">
<i
className={
activeChat === "global" ? "fas fa-globe" : "fas fa-user"
}
></i>
<h3>{getChatName()}</h3>
</div>
</div>
<div className="chat-messages">
{messages.length === 0 && (
<div className="message system">
<span className="msg-text">
NO_LOGS_FOUND. SECURE_LINE_READY...
</span>
</div>
)}
{messages.map((msg, index) => (
<div
key={msg.id || index}
className={`message ${msg.senderName === "System" ? "system" : ""}`}
>
<span className="msg-time">
[
{new Date(msg.createdAt).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}
]
</span>
<span className="msg-author">{msg.senderName}:</span>
<span className="msg-text">{msg.content}</span>
</div>
))}
<div ref={messagesEndRef} />
</div>
<div className="chat-input-area">
<input
type="text"
placeholder="TYPE_MESSAGE..."
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && sendMessage()}
/>
<button className="send-btn" onClick={sendMessage}>
<i className="fas fa-paper-plane"></i>
</button>
</div>
</main>
</div>
);
};
export default ChatTab;