import React, { useState, useEffect, useRef } from "react"; import Input from "../Input/Input"; import Button, { ToggleButton } from "../Input/Button"; import AuthModal from "../Auth/AuthModal"; import { useAuthModal } from "../../hooks/useAuthModal"; import { useAuth } from "../../context/AuthContext"; import { useSocket } from "../../context/SocketContext"; import { useChat } from "../../context/ChatContext"; import { ArrowLeftFromLineIcon, ArrowRightFromLineIcon, CrownIcon } from "lucide-react"; interface ChatMessage { chatter_username: string; message: string; time_sent: string; is_subscribed: boolean; } interface ChatPanelProps { streamId: number; onViewerCountChange?: (count: number) => void; } const ChatPanel: React.FC = ({ streamId, onViewerCountChange }) => { const { isLoggedIn, username, userId } = useAuth(); const { showChat, setShowChat } = useChat(); const { showAuthModal, setShowAuthModal } = useAuthModal(); const { socket, isConnected } = useSocket(); const [messages, setMessages] = useState([]); const [inputMessage, setInputMessage] = useState(""); const chatContainerRef = useRef(null); const [justToggled, setJustToggled] = useState(false); const subscribedUsersRef = useRef>({}); // Join chat room when component mounts useEffect(() => { if (socket && isConnected) { // Add username check socket.emit("join", { userId: userId ? userId : null, username: username ? username : "Guest", stream_id: streamId, }); // Handle beforeunload event const handleBeforeUnload = () => { socket.emit("leave", { userId: userId ? userId : null, username: username ? username : "Guest", stream_id: streamId, }); socket.disconnect(); }; window.addEventListener("beforeunload", handleBeforeUnload); // Load initial chat history fetch(`/api/chat/${streamId}`) .then((response) => { if (!response.ok) throw new Error("Failed to fetch chat history"); return response.json(); }) .then((data) => { if (data.chat_history) { // Store subscription status for each user data.chat_history.forEach((msg: ChatMessage) => { if (msg.is_subscribed) { subscribedUsersRef.current[msg.chatter_username] = true; } }); setMessages(data.chat_history); } }) .catch((error) => { console.error("Error loading chat history:", error); }); // Handle incoming messages socket.on("new_message", (data: ChatMessage) => { // If subscription info is missing, use our stored knowledge if (data.is_subscribed === undefined) { data.is_subscribed = !!subscribedUsersRef.current[data.chatter_username]; } else if (data.is_subscribed) { // Update our subscription tracking subscribedUsersRef.current[data.chatter_username] = true; } setMessages((prev) => [...prev, data]); }); // Handle live viewership socket.on("status", (data: any) => { if (onViewerCountChange && data.num_viewers) { onViewerCountChange(data.num_viewers); } }); // Cleanup function return () => { window.removeEventListener("beforeunload", handleBeforeUnload); socket.emit("leave", { stream_id: streamId }); socket.disconnect(); }; } }, [socket, isConnected, userId, username, streamId]); // Auto-scroll to bottom when new messages arrive useEffect(() => { if (chatContainerRef.current) { chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight; } }, [messages]); // Keyboard shortcut to toggle chat useEffect(() => { const handleKeyPress = (e: KeyboardEvent) => { if (e.key === "c" && document.activeElement == document.body) { toggleChat(); } }; document.addEventListener("keydown", handleKeyPress); return () => { document.removeEventListener("keydown", handleKeyPress); }; }, [showChat]); const toggleChat = () => { setShowChat(!showChat); setJustToggled(true); setTimeout(() => setJustToggled(false), 200); }; const sendChat = () => { if (!inputMessage.trim() || !socket || !isConnected) { console.log("Invalid message or socket not connected"); return; } socket.emit("send_message", { username: username, stream_id: streamId, message: inputMessage.trim(), }); setInputMessage(""); }; const handleKeyPress = (e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); sendChat(); } }; return (
{/* Toggle Button for Chat */} {/* Chat Header */}

Stream Chat

{/* Message List */}
{messages.map((msg, index) => (
{/* User avatar with image */}
(msg.chatter_username === username ? null : (window.location.href = `/user/${msg.chatter_username}`))} > { e.currentTarget.src = "/images/pfps/default.png"; e.currentTarget.onerror = null; }} alt="User Avatar" className="w-full h-full object-cover" style={{ width: "2.5em", height: "2.5em" }} />
{/* Username */} (msg.chatter_username === username ? null : (window.location.href = `/user/${msg.chatter_username}`))} > {msg.chatter_username} {msg.is_subscribed && }
{/* Message content */}
{msg.message}
{/* Time sent */}
{new Date(msg.time_sent).toLocaleTimeString("en-GB", { hour12: false, hour: "2-digit", minute: "2-digit", })}
))}
{/* Input area */}
{isLoggedIn ? ( <> setInputMessage(e.target.value)} onKeyDown={handleKeyPress} placeholder="Type a message..." extraClasses="flex-grow w-full focus:w-full" maxLength={200} onClick={() => !isLoggedIn && setShowAuthModal(true)} /> ) : ( )}
{showAuthModal && setShowAuthModal(false)} />}
); }; export default ChatPanel;