fix: corrige contador de mensagens nao lidas e chat desktop abrindo expandido

- 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 <noreply@anthropic.com>
This commit is contained in:
rever-tecnologia 2025-12-11 16:16:23 -03:00
parent 082f2d67f1
commit efc3af3fde
2 changed files with 49 additions and 14 deletions

View file

@ -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) // 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 // O estado inicial isMinimized=true e definido no useState e nao deve ser sobrescrito na montagem
useEffect(() => { 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 = () => { const handler = () => {
// Ignorar a primeira chamada para preservar o estado inicial (isMinimized=true) // Ignorar eventos de resize durante o periodo de estabilizacao
// Isso evita condicao de corrida onde window.innerHeight pode estar incorreto antes do Tauri aplicar o tamanho if (Date.now() - mountTime < STABILIZATION_DELAY) {
if (isFirstRender) {
isFirstRender = false
return return
} }
const h = window.innerHeight const h = window.innerHeight

View file

@ -401,22 +401,54 @@ export function ChatWidget() {
} }
}, [messages.length, isOpen, isMinimized]) }, [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<boolean>(false)
// Reset da flag quando fecha ou minimiza o chat
useEffect(() => { useEffect(() => {
if (!viewerId || !chat || !activeTicketId) return if (!isOpen || isMinimized) {
// Só marca quando o widget está aberto, expandido e a aba está ativa 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 (!isOpen || isMinimized) return
if (!viewerId || !activeTicketId) return
if (typeof document !== "undefined" && document.visibilityState === "hidden") 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 const unreadIds = chat.messages
?.filter((msg) => !msg.readBy?.some((r) => r.userId === viewerId)) ?.filter((msg) => !msg.readBy?.some((r) => r.userId === viewerId))
.map((msg) => msg.id) ?? [] .map((msg) => msg.id) ?? []
if (unreadIds.length === 0) return
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({ markChatRead({
ticketId: activeTicketId as Id<"tickets">, ticketId: activeTicketId as Id<"tickets">,
actorId: viewerId as Id<"users">, actorId: viewerId as Id<"users">,
messageIds: unreadIds, messageIds: unreadIds,
}).catch(console.error) })
.then(() => {
hasMarkedReadRef.current = true
})
.catch(console.error)
}, 100)
return () => clearTimeout(timeoutId)
}, [viewerId, chat, activeTicketId, isOpen, isMinimized, markChatRead]) }, [viewerId, chat, activeTicketId, isOpen, isMinimized, markChatRead])
// Upload de arquivos // Upload de arquivos