Sync active chat on shared websocket

This commit is contained in:
lizerk 2026-04-02 10:20:46 +03:00
parent f03887aa00
commit 4b179fe801
3 changed files with 87 additions and 0 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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<MessageHandler> = new Set();
private statusHandlers: Set<StatusHandler> = new Set();
private isConnecting = false;
@ -58,6 +59,27 @@ class WebSocketManager {
});
}
private sendJson(payload: Record<string, unknown>) {
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;