From efc3af3fde60e1ae4cb9707a7677ffa37796131d Mon Sep 17 00:00:00 2001 From: rever-tecnologia Date: Thu, 11 Dec 2025 16:16:23 -0300 Subject: [PATCH] fix: corrige contador de mensagens nao lidas e chat desktop abrindo expandido MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Web: adiciona ref hasMarkedReadRef para evitar chamadas duplicadas ao markChatRead e garante que mensagens sejam marcadas como lidas mesmo quando o chat carrega apos isOpen se tornar true - Desktop: aumenta periodo de estabilizacao do resize handler para 500ms, evitando que eventos transitórios alterem o estado isMinimized 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- apps/desktop/src/chat/ChatWidget.tsx | 13 +++++--- src/components/chat/chat-widget.tsx | 50 +++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/apps/desktop/src/chat/ChatWidget.tsx b/apps/desktop/src/chat/ChatWidget.tsx index 333ed78..2c3b65b 100644 --- a/apps/desktop/src/chat/ChatWidget.tsx +++ b/apps/desktop/src/chat/ChatWidget.tsx @@ -133,12 +133,15 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) { // Sincroniza estado de minimizado com o tamanho da janela (apenas em resizes reais, nao na montagem) // O estado inicial isMinimized=true e definido no useState e nao deve ser sobrescrito na montagem useEffect(() => { - let isFirstRender = true + // Ignorar todos os eventos de resize nos primeiros 500ms apos a montagem + // Isso da tempo ao Tauri de aplicar o tamanho correto da janela + // e evita que resizes transitórios durante a criação da janela alterem o estado + const mountTime = Date.now() + const STABILIZATION_DELAY = 500 // ms para a janela estabilizar + const handler = () => { - // Ignorar a primeira chamada para preservar o estado inicial (isMinimized=true) - // Isso evita condicao de corrida onde window.innerHeight pode estar incorreto antes do Tauri aplicar o tamanho - if (isFirstRender) { - isFirstRender = false + // Ignorar eventos de resize durante o periodo de estabilizacao + if (Date.now() - mountTime < STABILIZATION_DELAY) { return } const h = window.innerHeight diff --git a/src/components/chat/chat-widget.tsx b/src/components/chat/chat-widget.tsx index a3b024d..0363098 100644 --- a/src/components/chat/chat-widget.tsx +++ b/src/components/chat/chat-widget.tsx @@ -401,22 +401,54 @@ export function ChatWidget() { } }, [messages.length, isOpen, isMinimized]) - // Marcar mensagens como lidas ao abrir/mostrar chat + // Ref para rastrear se ja marcamos como lidas nesta abertura do chat + const hasMarkedReadRef = useRef(false) + + // Reset da flag quando fecha ou minimiza o chat useEffect(() => { - if (!viewerId || !chat || !activeTicketId) return - // Só marca quando o widget está aberto, expandido e a aba está ativa + if (!isOpen || isMinimized) { + hasMarkedReadRef.current = false + } + }, [isOpen, isMinimized]) + + // Marcar mensagens como lidas ao abrir/mostrar chat + // Usa um pequeno delay para garantir que o chat carregou + useEffect(() => { + // So marca quando o widget esta aberto, expandido e a aba esta ativa if (!isOpen || isMinimized) return + if (!viewerId || !activeTicketId) return if (typeof document !== "undefined" && document.visibilityState === "hidden") return + // Se ainda nao temos chat carregado, aguardar + if (!chat) return + + // Evitar marcar multiplas vezes na mesma abertura + if (hasMarkedReadRef.current) return + const unreadIds = chat.messages ?.filter((msg) => !msg.readBy?.some((r) => r.userId === viewerId)) .map((msg) => msg.id) ?? [] - if (unreadIds.length === 0) return - markChatRead({ - ticketId: activeTicketId as Id<"tickets">, - actorId: viewerId as Id<"users">, - messageIds: unreadIds, - }).catch(console.error) + + if (unreadIds.length === 0) { + // Mesmo sem mensagens nao lidas, marcar que ja processamos + hasMarkedReadRef.current = true + return + } + + // Marcar como lidas com pequeno delay para garantir estabilidade + const timeoutId = setTimeout(() => { + markChatRead({ + ticketId: activeTicketId as Id<"tickets">, + actorId: viewerId as Id<"users">, + messageIds: unreadIds, + }) + .then(() => { + hasMarkedReadRef.current = true + }) + .catch(console.error) + }, 100) + + return () => clearTimeout(timeoutId) }, [viewerId, chat, activeTicketId, isOpen, isMinimized, markChatRead]) // Upload de arquivos