Added chat message clear
This commit is contained in:
parent
df88374999
commit
ec375730f0
@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useEffect, useRef, KeyboardEvent } from "react";
|
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 SendIcon from "@mui/icons-material/Send";
|
||||||
|
import ClearIcon from "@mui/icons-material/Clear";
|
||||||
import useWebSocket from "react-use-websocket";
|
import useWebSocket from "react-use-websocket";
|
||||||
import { Session } from "./GlobalContext";
|
import { Session } from "./GlobalContext";
|
||||||
import "./LobbyChat.css";
|
import "./LobbyChat.css";
|
||||||
@ -23,6 +24,7 @@ type LobbyChatProps = {
|
|||||||
const LobbyChat: React.FC<LobbyChatProps> = ({ socketUrl, session, lobbyId }) => {
|
const LobbyChat: React.FC<LobbyChatProps> = ({ socketUrl, session, lobbyId }) => {
|
||||||
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
||||||
const [newMessage, setNewMessage] = useState<string>("");
|
const [newMessage, setNewMessage] = useState<string>("");
|
||||||
|
const [isClearing, setIsClearing] = useState<boolean>(false);
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
@ -38,12 +40,16 @@ const LobbyChat: React.FC<LobbyChatProps> = ({ socketUrl, session, lobbyId }) =>
|
|||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case "chat_message":
|
case "chat_message":
|
||||||
const chatMessage = data as ChatMessage;
|
const chatMessage = data as ChatMessage;
|
||||||
setMessages(prev => [...prev, chatMessage]);
|
setMessages((prev) => [...prev, chatMessage]);
|
||||||
break;
|
break;
|
||||||
case "chat_messages":
|
case "chat_messages":
|
||||||
const chatMessages = data.messages as ChatMessage[];
|
const chatMessages = data.messages as ChatMessage[];
|
||||||
setMessages(chatMessages);
|
setMessages(chatMessages);
|
||||||
break;
|
break;
|
||||||
|
case "chat_cleared":
|
||||||
|
// Clear the messages when receiving chat cleared event
|
||||||
|
setMessages([]);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -88,9 +94,9 @@ const LobbyChat: React.FC<LobbyChatProps> = ({ socketUrl, session, lobbyId }) =>
|
|||||||
const formatTimestamp = (timestamp: number): string => {
|
const formatTimestamp = (timestamp: number): string => {
|
||||||
const date = new Date(timestamp * 1000);
|
const date = new Date(timestamp * 1000);
|
||||||
return date.toLocaleTimeString([], {
|
return date.toLocaleTimeString([], {
|
||||||
hour: '2-digit',
|
hour: "2-digit",
|
||||||
minute: '2-digit',
|
minute: "2-digit",
|
||||||
second: '2-digit'
|
second: "2-digit",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,26 +104,57 @@ const LobbyChat: React.FC<LobbyChatProps> = ({ socketUrl, session, lobbyId }) =>
|
|||||||
return message.sender_session_id === session.id;
|
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 (
|
return (
|
||||||
<Paper className="lobby-chat" sx={{ height: 400, display: "flex", flexDirection: "column", p: 1 }}>
|
<Paper className="lobby-chat" sx={{ height: 400, display: "flex", flexDirection: "column", p: 1 }}>
|
||||||
<Typography variant="h6" sx={{ mb: 1, textAlign: "center" }}>
|
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1 }}>
|
||||||
Chat
|
<Typography variant="h6" sx={{ textAlign: "center", flexGrow: 1 }}>
|
||||||
</Typography>
|
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 }}>
|
<Box className="chat-messages" sx={{ flexGrow: 1, overflowY: "auto", mb: 1 }}>
|
||||||
<List dense>
|
<List dense>
|
||||||
{messages.length === 0 ? (
|
{messages.length === 0 ? (
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemText
|
<ListItemText primary="No messages yet" sx={{ textAlign: "center", color: "text.secondary" }} />
|
||||||
primary="No messages yet"
|
|
||||||
sx={{ textAlign: "center", color: "text.secondary" }}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
) : (
|
) : (
|
||||||
messages.map((message) => (
|
messages.map((message) => (
|
||||||
<ListItem
|
<ListItem
|
||||||
key={message.id}
|
key={message.id}
|
||||||
className={`chat-message ${isOwnMessage(message) ? 'own-message' : 'other-message'}`}
|
className={`chat-message ${isOwnMessage(message) ? "own-message" : "other-message"}`}
|
||||||
sx={{
|
sx={{
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
alignItems: isOwnMessage(message) ? "flex-end" : "flex-start",
|
alignItems: isOwnMessage(message) ? "flex-end" : "flex-start",
|
||||||
@ -162,12 +199,7 @@ const LobbyChat: React.FC<LobbyChatProps> = ({ socketUrl, session, lobbyId }) =>
|
|||||||
multiline
|
multiline
|
||||||
maxRows={3}
|
maxRows={3}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton color="primary" onClick={handleSendMessage} disabled={!newMessage.trim()} sx={{ mb: 0.5 }}>
|
||||||
color="primary"
|
|
||||||
onClick={handleSendMessage}
|
|
||||||
disabled={!newMessage.trim()}
|
|
||||||
sx={{ mb: 0.5 }}
|
|
||||||
>
|
|
||||||
<SendIcon />
|
<SendIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -196,6 +196,29 @@ class Lobby:
|
|||||||
with self.lock:
|
with self.lock:
|
||||||
return self.chat_messages[-limit:] if self.chat_messages else []
|
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:
|
async def broadcast_chat_message(self, chat_message: ChatMessageModel) -> None:
|
||||||
"""Broadcast a chat message to all connected sessions in the lobby"""
|
"""Broadcast a chat message to all connected sessions in the lobby"""
|
||||||
failed_sessions: List[Session] = []
|
failed_sessions: List[Session] = []
|
||||||
|
@ -262,6 +262,35 @@ class SendChatMessageHandler(MessageHandler):
|
|||||||
await lobby.broadcast_chat_message(chat_message)
|
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):
|
class RelayICECandidateHandler(MessageHandler):
|
||||||
"""Handler for relayICECandidate messages - WebRTC signaling"""
|
"""Handler for relayICECandidate messages - WebRTC signaling"""
|
||||||
|
|
||||||
@ -337,6 +366,7 @@ class MessageRouter:
|
|||||||
self.register("list_users", ListUsersHandler())
|
self.register("list_users", ListUsersHandler())
|
||||||
self.register("get_chat_messages", GetChatMessagesHandler())
|
self.register("get_chat_messages", GetChatMessagesHandler())
|
||||||
self.register("send_chat_message", SendChatMessageHandler())
|
self.register("send_chat_message", SendChatMessageHandler())
|
||||||
|
self.register("clear_chat_messages", ClearChatMessagesHandler())
|
||||||
|
|
||||||
# WebRTC signaling handlers
|
# WebRTC signaling handlers
|
||||||
self.register("relayICECandidate", RelayICECandidateHandler())
|
self.register("relayICECandidate", RelayICECandidateHandler())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user