fix(chat): авто-сброс unread badge при открытом активном чате и синхронизация с сообщением

This commit is contained in:
2026-07-09 10:19:29 +03:00
parent d70827b39e
commit 7ed89f7f97

View File

@@ -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'] });
},
});