From 7ed89f7f97f111a96c3238eb850c3ad461f2423b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=D1=81=20=D0=A1=D1=83=D0=BB=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=D0=BE=D0=B2?= Date: Thu, 9 Jul 2026 10:19:29 +0300 Subject: [PATCH] =?UTF-8?q?fix(chat):=20=D0=B0=D0=B2=D1=82=D0=BE-=D1=81?= =?UTF-8?q?=D0=B1=D1=80=D0=BE=D1=81=20unread=20badge=20=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=20=D0=BE=D1=82=D0=BA=D1=80=D1=8B=D1=82=D0=BE=D0=BC=20=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=B8=D0=B2=D0=BD=D0=BE=D0=BC=20=D1=87=D0=B0=D1=82?= =?UTF-8?q?=D0=B5=20=D0=B8=20=D1=81=D0=B8=D0=BD=D1=85=D1=80=D0=BE=D0=BD?= =?UTF-8?q?=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=20=D1=81=D0=BE?= =?UTF-8?q?=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B5=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/Chat.tsx | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/client/src/pages/Chat.tsx b/client/src/pages/Chat.tsx index 66bb314..1a00915 100644 --- a/client/src/pages/Chat.tsx +++ b/client/src/pages/Chat.tsx @@ -1029,7 +1029,7 @@ export default function ChatPage() { markReadMutation.mutate(activeConvId); }, [activeConvId]); - // Auto-mark read when user returns to the tab with an open conversation + // Auto-mark read when user returns to the tab/window with an open conversation useEffect(() => { if (!activeConvId) return; const handleVisibility = () => { @@ -1037,8 +1037,15 @@ export default function ChatPage() { markReadMutation.mutate(activeConvId); } }; + const handleFocus = () => { + markReadMutation.mutate(activeConvId); + }; document.addEventListener('visibilitychange', handleVisibility); - return () => document.removeEventListener('visibilitychange', handleVisibility); + window.addEventListener('focus', handleFocus); + return () => { + document.removeEventListener('visibilitychange', handleVisibility); + window.removeEventListener('focus', handleFocus); + }; }, [activeConvId]); const activeConv = conversations.find(c => c.id === activeConvId) ?? null; @@ -1292,7 +1299,12 @@ export default function ChatPage() { useEvents({ onConvMessageCreated: useCallback((data: { conversationId: number; message: ConvMessage }) => { const { conversationId, message } = data; + const isActiveConv = conversationId === activeConvId; + const isMine = message.author?.id === user?.id; + const isVisible = typeof document !== 'undefined' && document.visibilityState === 'visible'; + // 1. Сначала добавляем сообщение в кэш активного чата, чтобы оно появилось + // одновременно со значком, а не после него. queryClient.setQueryData( ['/api/messenger/conversations', conversationId, 'messages'], (old: any) => { @@ -1303,18 +1315,8 @@ export default function ChatPage() { } ); - const isActiveConv = conversationId === activeConvId; - const isMine = message.author?.id === user?.id; - - // Auto-mark read when message arrives in the currently open conversation - if (isActiveConv && !isMine) { - apiRequest('POST', `/api/messenger/conversations/${conversationId}/read`).catch(() => {}); - // Also invalidate sidebar unread badge - queryClient.invalidateQueries({ queryKey: ['/api/messenger/unread-count'] }); - } - - // Update conv list (bump to top, update lastMessage, inc unread if not active) - // Also upsert missing conversation if not yet in cache + // 2. Обновляем список чатов: bump наверх, lastMessage, unreadCount. + // Для активного и видимого чата сразу сбрасываем unreadCount в 0. queryClient.setQueryData( ['/api/messenger/conversations'], (old: any) => { @@ -1333,7 +1335,7 @@ export default function ChatPage() { ...c, lastMessage: message, updatedAt: message.createdAt, - unreadCount: isActiveConv || isMine ? 0 : c.unreadCount + 1, + unreadCount: (isActiveConv && isVisible) || isMine ? 0 : c.unreadCount + 1, }; }); // Bump updated conversation to top (sort by updatedAt desc) @@ -1343,6 +1345,12 @@ export default function ChatPage() { return { ...old, conversations: sorted }; } ); + + // 3. Автоотметка прочтения только для активного, видимого чата и чужих сообщений. + // Используем mutation, чтобы onSuccess обновил кэш и сбросил бейджи. + if (isActiveConv && !isMine && isVisible) { + markReadMutation.mutate(conversationId); + } }, [activeConvId, user]), onConvMessageUpdated: useCallback((data: { conversationId: number; message: ConvMessage }) => { @@ -1484,6 +1492,7 @@ export default function ChatPage() { ), }; }); + queryClient.invalidateQueries({ queryKey: ['/api/messenger/unread-count'] }); }, });