From 4b179fe801a3b24f8884f6cef47b31f3528fc991 Mon Sep 17 00:00:00 2001 From: lizerk Date: Thu, 2 Apr 2026 10:20:46 +0300 Subject: [PATCH] Sync active chat on shared websocket --- frontend/src/app/messages-chatscope/page.tsx | 11 ++++ frontend/src/app/messages/[chatId]/page.tsx | 13 ++++ frontend/src/lib/websocketManager.ts | 63 ++++++++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/frontend/src/app/messages-chatscope/page.tsx b/frontend/src/app/messages-chatscope/page.tsx index d5131332..247532ba 100644 --- a/frontend/src/app/messages-chatscope/page.tsx +++ b/frontend/src/app/messages-chatscope/page.tsx @@ -22,6 +22,7 @@ import { apiClient } from '@/lib/api'; import { useUser } from '@/contexts/UserContext'; import { resolveMediaUrl, getWebSocketUrl } from '@/lib/url'; import NewChatModal from '@/components/NewChatModal'; +import wsManager from '@/lib/websocketManager'; import type { Chat as ChatType, Message as MessageType } from '@/types/api'; function getChatTitle(chat: ChatType, currentUserId?: number): string { @@ -144,6 +145,16 @@ export default function ChatscapeMessenger() { }, [selectedChatId]); // WebSocket для real-time обновлений + useEffect(() => { + if (!selectedChatId) return; + + wsManager.setActiveChat(selectedChatId); + + return () => { + wsManager.clearActiveChat(selectedChatId); + }; + }, [selectedChatId]); + useEffect(() => { if (!selectedChatId) return; diff --git a/frontend/src/app/messages/[chatId]/page.tsx b/frontend/src/app/messages/[chatId]/page.tsx index 3b07589e..fa4a90c9 100644 --- a/frontend/src/app/messages/[chatId]/page.tsx +++ b/frontend/src/app/messages/[chatId]/page.tsx @@ -18,6 +18,7 @@ import { useSilentChatReloadGuard } from "@/hooks/useSilentChatReloadGuard"; import { useWebSocket } from "@/hooks/useWebSocket"; import { uniqueMessagesById } from "@/lib/messages/chatUtils"; import { formatDayDivider, getMessagePreviewText, getReplyToId } from "@/lib/messages/messageUtils"; +import wsManager from "@/lib/websocketManager"; import ScrollableMessageList, { ScrollableMessageListInner } from "@/components/ScrollableMessageList"; type ReplyTarget = { @@ -155,6 +156,18 @@ export default function MessageDialogPage() { onReconnectExhausted: handleReconnectExhausted, }); + useEffect(() => { + if (!chatId || Number.isNaN(chatId)) { + return; + } + + wsManager.setActiveChat(chatId); + + return () => { + wsManager.clearActiveChat(chatId); + }; + }, [chatId]); + useEffect(() => { if (!isConnected) { return; diff --git a/frontend/src/lib/websocketManager.ts b/frontend/src/lib/websocketManager.ts index a4fb69c0..1ddd70c3 100644 --- a/frontend/src/lib/websocketManager.ts +++ b/frontend/src/lib/websocketManager.ts @@ -21,6 +21,7 @@ class WebSocketManager { private reconnectTimeout: NodeJS.Timeout | null = null; private reconnectAttempts = 0; private maxReconnectAttempts = 5; + private activeChatId: number | null = null; private messageHandlers: Set = new Set(); private statusHandlers: Set = new Set(); private isConnecting = false; @@ -58,6 +59,27 @@ class WebSocketManager { }); } + private sendJson(payload: Record) { + if (this.ws?.readyState !== WebSocket.OPEN) { + return false; + } + + this.ws.send(JSON.stringify(payload)); + return true; + } + + private syncActiveChatOnSocket() { + if (this.activeChatId === null) { + return; + } + + this.sendJson({ + action: 'open_chat', + chat_id: this.activeChatId, + load_history: false, + }); + } + connect() { // Если уже подключены или подключаемся, не создаем новое соединение if (this.ws?.readyState === WebSocket.OPEN || this.isConnecting) { @@ -109,6 +131,7 @@ class WebSocketManager { console.log('[WS Manager] Connected'); this.isConnecting = false; this.reconnectAttempts = 0; + this.syncActiveChatOnSocket(); this.notifyStatus(); }; @@ -180,6 +203,46 @@ class WebSocketManager { this.notifyStatus(); } + setActiveChat(chatId: number) { + if (!Number.isFinite(chatId) || chatId <= 0) { + return; + } + + if (this.activeChatId === chatId) { + return; + } + + const previousChatId = this.activeChatId; + this.activeChatId = chatId; + + if (previousChatId !== null) { + this.sendJson({ + action: 'close_chat', + chat_id: previousChatId, + }); + } + + this.syncActiveChatOnSocket(); + } + + clearActiveChat(chatId?: number) { + if (this.activeChatId === null) { + return; + } + + if (typeof chatId === 'number' && this.activeChatId !== chatId) { + return; + } + + const chatToClose = this.activeChatId; + this.activeChatId = null; + + this.sendJson({ + action: 'close_chat', + chat_id: chatToClose, + }); + } + subscribe(handler: MessageHandler) { this.messageHandlers.add(handler); this.shouldReconnect = true;