fix: automações (gatilhos, histórico) e chat desktop

This commit is contained in:
esdrasrenan 2025-12-13 11:26:42 -03:00
parent 8ab510bfe9
commit e4d0c95791
7 changed files with 670 additions and 53 deletions

View file

@ -4,7 +4,15 @@ import { openUrl as openExternal } from "@tauri-apps/plugin-opener"
import { invoke } from "@tauri-apps/api/core"
import { listen } from "@tauri-apps/api/event"
import { Send, X, Loader2, MessageCircle, Paperclip, FileText, Image as ImageIcon, File, User, ChevronUp, Minimize2, Eye, Download, Check } from "lucide-react"
import type { ChatAttachment, ChatMessage, ChatMessagesResponse, NewMessageEvent, SessionEndedEvent, UnreadUpdateEvent } from "./types"
import type {
ChatAttachment,
ChatMessage,
ChatMessagesResponse,
NewMessageEvent,
SessionEndedEvent,
SessionStartedEvent,
UnreadUpdateEvent,
} from "./types"
import { getMachineStoreConfig } from "./machineStore"
const MAX_MESSAGES_IN_MEMORY = 200 // Limite de mensagens para evitar memory leak
@ -243,6 +251,7 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
const messagesContainerRef = useRef<HTMLDivElement>(null)
const messageElementsRef = useRef<Map<string, HTMLDivElement>>(new Map())
const prevHasSessionRef = useRef<boolean>(false)
const retryDelayMsRef = useRef<number>(1_000)
const [isAtBottom, setIsAtBottom] = useState(true)
const isAtBottomRef = useRef(true)
@ -361,6 +370,7 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
}
setError(null)
retryDelayMsRef.current = 1_000
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
setError(message || "Erro ao carregar mensagens.")
@ -369,6 +379,22 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
}
}, [ensureConfig, ticketId, ticketRef])
// Auto-retry leve quando houver erro (evita ficar "morto" após falha transiente).
useEffect(() => {
if (!error) return
const delayMs = retryDelayMsRef.current
const timeout = window.setTimeout(() => {
loadMessages()
}, delayMs)
retryDelayMsRef.current = Math.min(retryDelayMsRef.current * 2, 30_000)
return () => {
window.clearTimeout(timeout)
}
}, [error, loadMessages])
const markUnreadMessagesRead = useCallback(async () => {
if (unreadCount <= 0) return
const ids = getUnreadAgentMessageIds(messages, unreadCount)
@ -420,6 +446,24 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
}
}, [ticketId, loadMessages])
// Recarregar quando uma nova sessão iniciar (usuário pode estar com o chat aberto em "Offline")
useEffect(() => {
let unlisten: (() => void) | null = null
listen<SessionStartedEvent>("raven://chat/session-started", (event) => {
if (event.payload?.session?.ticketId === ticketId) {
loadMessages()
}
})
.then((u) => {
unlisten = u
})
.catch((err) => console.error("Falha ao registrar listener session-started:", err))
return () => {
unlisten?.()
}
}, [ticketId, loadMessages])
// Atualizar contador em tempo real (inclui decremento quando a máquina marca como lida)
useEffect(() => {
let unlisten: (() => void) | null = null
@ -697,10 +741,15 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
// Mostrar chip compacto de erro (compativel com janela minimizada)
return (
<div className="pointer-events-none flex h-full w-full items-end justify-end bg-transparent p-2">
<div className="pointer-events-auto flex items-center gap-2 rounded-full bg-red-100 px-4 py-2 text-red-600 shadow-lg">
<button
type="button"
onClick={() => loadMessages()}
className="pointer-events-auto flex items-center gap-2 rounded-full bg-red-100 px-4 py-2 text-red-600 shadow-lg transition hover:bg-red-200/60"
title="Tentar novamente"
>
<X className="size-4" />
<span className="text-sm font-medium">Erro no chat</span>
</div>
</button>
</div>
)
}