Added chat message clear

This commit is contained in:
James Ketr 2025-09-07 23:23:19 -07:00
parent df88374999
commit ec375730f0
3 changed files with 109 additions and 24 deletions

View File

@ -1,6 +1,7 @@
import React, { useState, useEffect, useRef, KeyboardEvent } from "react";
import { Paper, TextField, List, ListItem, ListItemText, Typography, Box, IconButton } from "@mui/material";
import { Paper, TextField, List, ListItem, ListItemText, Typography, Box, IconButton, Button } from "@mui/material";
import SendIcon from "@mui/icons-material/Send";
import ClearIcon from "@mui/icons-material/Clear";
import useWebSocket from "react-use-websocket";
import { Session } from "./GlobalContext";
import "./LobbyChat.css";
@ -23,6 +24,7 @@ type LobbyChatProps = {
const LobbyChat: React.FC<LobbyChatProps> = ({ socketUrl, session, lobbyId }) => {
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [newMessage, setNewMessage] = useState<string>("");
const [isClearing, setIsClearing] = useState<boolean>(false);
const messagesEndRef = useRef<HTMLDivElement>(null);
const scrollToBottom = () => {
@ -34,16 +36,20 @@ const LobbyChat: React.FC<LobbyChatProps> = ({ socketUrl, session, lobbyId }) =>
onMessage: (event: MessageEvent) => {
const message = JSON.parse(event.data);
const data: any = message.data;
switch (message.type) {
case "chat_message":
const chatMessage = data as ChatMessage;
setMessages(prev => [...prev, chatMessage]);
setMessages((prev) => [...prev, chatMessage]);
break;
case "chat_messages":
const chatMessages = data.messages as ChatMessage[];
setMessages(chatMessages);
break;
case "chat_cleared":
// Clear the messages when receiving chat cleared event
setMessages([]);
break;
default:
break;
}
@ -87,10 +93,10 @@ const LobbyChat: React.FC<LobbyChatProps> = ({ socketUrl, session, lobbyId }) =>
const formatTimestamp = (timestamp: number): string => {
const date = new Date(timestamp * 1000);
return date.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
return date.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
});
};
@ -98,26 +104,57 @@ const LobbyChat: React.FC<LobbyChatProps> = ({ socketUrl, session, lobbyId }) =>
return message.sender_session_id === session.id;
};
const handleClearChat = async () => {
if (!lobbyId || isClearing) {
return;
}
try {
setIsClearing(true);
// Use websocket instead of HTTP API to ensure all clients receive the clear event
sendJsonMessage({
type: "clear_chat_messages",
});
// Note: We don't clear messages locally here as we'll receive the chat_cleared event
} catch (error) {
console.error("Failed to clear chat messages:", error);
} finally {
setIsClearing(false);
}
};
return (
<Paper className="lobby-chat" sx={{ height: 400, display: "flex", flexDirection: "column", p: 1 }}>
<Typography variant="h6" sx={{ mb: 1, textAlign: "center" }}>
Chat
</Typography>
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1 }}>
<Typography variant="h6" sx={{ textAlign: "center", flexGrow: 1 }}>
Chat
</Typography>
{messages.length > 0 && (
<Button
size="small"
startIcon={<ClearIcon />}
onClick={handleClearChat}
disabled={isClearing}
sx={{ minWidth: "auto", ml: 1 }}
color="secondary"
variant="outlined"
>
{isClearing ? "Clearing..." : "Clear"}
</Button>
)}
</Box>
<Box className="chat-messages" sx={{ flexGrow: 1, overflowY: "auto", mb: 1 }}>
<List dense>
{messages.length === 0 ? (
<ListItem>
<ListItemText
primary="No messages yet"
sx={{ textAlign: "center", color: "text.secondary" }}
/>
<ListItemText primary="No messages yet" sx={{ textAlign: "center", color: "text.secondary" }} />
</ListItem>
) : (
messages.map((message) => (
<ListItem
key={message.id}
className={`chat-message ${isOwnMessage(message) ? 'own-message' : 'other-message'}`}
<ListItem
key={message.id}
className={`chat-message ${isOwnMessage(message) ? "own-message" : "other-message"}`}
sx={{
flexDirection: "column",
alignItems: isOwnMessage(message) ? "flex-end" : "flex-start",
@ -162,12 +199,7 @@ const LobbyChat: React.FC<LobbyChatProps> = ({ socketUrl, session, lobbyId }) =>
multiline
maxRows={3}
/>
<IconButton
color="primary"
onClick={handleSendMessage}
disabled={!newMessage.trim()}
sx={{ mb: 0.5 }}
>
<IconButton color="primary" onClick={handleSendMessage} disabled={!newMessage.trim()} sx={{ mb: 0.5 }}>
<SendIcon />
</IconButton>
</Box>

View File

@ -196,6 +196,29 @@ class Lobby:
with self.lock:
return self.chat_messages[-limit:] if self.chat_messages else []
def clear_chat_messages(self) -> None:
"""Clear all chat messages from the lobby"""
with self.lock:
self.chat_messages.clear()
async def broadcast_chat_clear(self) -> None:
"""Broadcast a chat clear event to all connected sessions in the lobby"""
failed_sessions: List[Session] = []
for peer in self.sessions.values():
if peer.ws:
try:
logger.info(f"{self.getName()} -> chat_cleared({peer.getName()})")
await peer.ws.send_json({"type": "chat_cleared", "data": {}})
except Exception as e:
logger.warning(
f"Failed to send chat clear message to {peer.getName()}: {e}"
)
failed_sessions.append(peer)
# Clean up failed sessions
for failed_session in failed_sessions:
failed_session.ws = None
async def broadcast_chat_message(self, chat_message: ChatMessageModel) -> None:
"""Broadcast a chat message to all connected sessions in the lobby"""
failed_sessions: List[Session] = []

View File

@ -262,6 +262,35 @@ class SendChatMessageHandler(MessageHandler):
await lobby.broadcast_chat_message(chat_message)
class ClearChatMessagesHandler(MessageHandler):
"""Handler for clear_chat_messages messages"""
async def handle(
self,
session: "Session",
lobby: "Lobby",
data: Dict[str, Any],
websocket: WebSocket,
managers: Dict[str, Any],
) -> None:
if not session.name:
logger.error(
f"{session.getName()} - Cannot clear chat messages without name"
)
await websocket.send_json(
{
"type": "error",
"data": {"error": "Must set name before clearing chat messages"},
}
)
return
# Clear the messages and broadcast the clear event
lobby.clear_chat_messages()
logger.info(f"{session.getName()} -> clear_chat_messages({lobby.getName()})")
await lobby.broadcast_chat_clear()
class RelayICECandidateHandler(MessageHandler):
"""Handler for relayICECandidate messages - WebRTC signaling"""
@ -337,6 +366,7 @@ class MessageRouter:
self.register("list_users", ListUsersHandler())
self.register("get_chat_messages", GetChatMessagesHandler())
self.register("send_chat_message", SendChatMessageHandler())
self.register("clear_chat_messages", ClearChatMessagesHandler())
# WebRTC signaling handlers
self.register("relayICECandidate", RelayICECandidateHandler())