fix: corrige sincronizacao de estado do chat entre abas

O BroadcastChannel nao estava funcionando corretamente porque
cada aba restaurava do localStorage independentemente na montagem.

Mudanca:
- Substituido BroadcastChannel pelo evento 'storage' do localStorage
- O evento storage dispara automaticamente em TODAS as outras abas
  quando o localStorage e alterado (mais confiavel)
- Removido broadcastChannelRef e CHAT_WIDGET_CHANNEL nao mais usados

Comportamento:
- Abrir/fechar/minimizar chat em uma aba sincroniza com todas as outras
- Estado persiste entre reloads via localStorage

🤖 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 15:48:33 -03:00
parent 366bc4bf12
commit 082f2d67f1

View file

@ -38,7 +38,6 @@ import {
const MAX_MESSAGE_LENGTH = 4000 const MAX_MESSAGE_LENGTH = 4000
const MAX_ATTACHMENT_SIZE = 5 * 1024 * 1024 // 5MB const MAX_ATTACHMENT_SIZE = 5 * 1024 * 1024 // 5MB
const MAX_ATTACHMENTS = 5 const MAX_ATTACHMENTS = 5
const CHAT_WIDGET_CHANNEL = "chat-widget-sync"
const STORAGE_KEY = "chat-widget-state" const STORAGE_KEY = "chat-widget-state"
type ChatWidgetState = { type ChatWidgetState = {
@ -281,7 +280,6 @@ export function ChatWidget() {
return null return null
}) })
const [draft, setDraft] = useState("") const [draft, setDraft] = useState("")
const broadcastChannelRef = useRef<BroadcastChannel | null>(null)
const [isSending, setIsSending] = useState(false) const [isSending, setIsSending] = useState(false)
const [isEndingChat, setIsEndingChat] = useState(false) const [isEndingChat, setIsEndingChat] = useState(false)
const [attachments, setAttachments] = useState<UploadedFile[]>([]) const [attachments, setAttachments] = useState<UploadedFile[]>([])
@ -320,31 +318,32 @@ export function ChatWidget() {
const machineOnline = liveChat?.machineOnline ?? false const machineOnline = liveChat?.machineOnline ?? false
const machineHostname = liveChat?.machineHostname const machineHostname = liveChat?.machineHostname
// Sincronizar estado entre abas usando BroadcastChannel // Sincronizar estado entre abas usando evento storage do localStorage
// O evento storage dispara automaticamente em TODAS as outras abas quando localStorage muda
useEffect(() => { useEffect(() => {
if (typeof window === "undefined") return if (typeof window === "undefined") return
// Criar canal de broadcast const handleStorageChange = (event: StorageEvent) => {
const channel = new BroadcastChannel(CHAT_WIDGET_CHANNEL) // Ignorar mudancas em outras chaves
broadcastChannelRef.current = channel if (event.key !== STORAGE_KEY) return
// Ignorar se nao tem valor novo
if (!event.newValue) return
// Ouvir mensagens de outras abas try {
channel.onmessage = (event: MessageEvent<ChatWidgetState>) => { const state = JSON.parse(event.newValue) as ChatWidgetState
const state = event.data setIsOpen(state.isOpen)
setIsOpen(state.isOpen) setIsMinimized(state.isMinimized)
setIsMinimized(state.isMinimized) if (state.activeTicketId) {
if (state.activeTicketId) { setActiveTicketId(state.activeTicketId)
setActiveTicketId(state.activeTicketId) }
} } catch {}
} }
return () => { window.addEventListener("storage", handleStorageChange)
channel.close() return () => window.removeEventListener("storage", handleStorageChange)
broadcastChannelRef.current = null
}
}, []) }, [])
// Salvar estado no localStorage e broadcast para outras abas quando muda // Salvar estado no localStorage quando muda (dispara evento storage em outras abas)
useEffect(() => { useEffect(() => {
if (typeof window === "undefined") return if (typeof window === "undefined") return
@ -354,15 +353,10 @@ export function ChatWidget() {
activeTicketId, activeTicketId,
} }
// Salvar no localStorage para persistir entre reloads // Salvar no localStorage (isso dispara evento storage em outras abas automaticamente)
try { try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state)) localStorage.setItem(STORAGE_KEY, JSON.stringify(state))
} catch {} } catch {}
// Broadcast para outras abas
if (broadcastChannelRef.current) {
broadcastChannelRef.current.postMessage(state)
}
}, [isOpen, isMinimized, activeTicketId]) }, [isOpen, isMinimized, activeTicketId])
// Auto-selecionar primeira sessão se nenhuma selecionada // Auto-selecionar primeira sessão se nenhuma selecionada