234 lines
7.2 KiB
JavaScript
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;
|