feat(desktop): implementa Convex React subscriptions para chat em tempo real

- Adiciona ConvexMachineProvider para autenticacao via machine token
- Cria hooks customizados (useMachineSessions, useMachineMessages, etc)
- Refatora ChatWidget e ChatHubWidget para usar useQuery/useMutation
- Remove polling e dependencia de Tauri events para mensagens
- Adiciona copia local dos arquivos _generated do Convex
- Remove componentes obsoletos (ChatSessionItem, ChatSessionList)

Beneficios:
- Tempo real verdadeiro via WebSocket (sem polling)
- Melhor escalabilidade e performance
- Codigo mais simples e maintivel
- Consistencia de estado entre multiplas janelas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
esdrasrenan 2025-12-15 23:40:34 -03:00
parent a6af4aa580
commit c51b08f127
16 changed files with 1181 additions and 650 deletions

View file

@ -1,20 +1,63 @@
import { ConvexProvider } from "convex/react"
import { ChatWidget } from "./ChatWidget"
import { ChatHubWidget } from "./ChatHubWidget"
import { ConvexMachineProvider, useConvexMachine } from "./ConvexMachineProvider"
import { Loader2 } from "lucide-react"
function ChatAppContent() {
const { client, isReady, error } = useConvexMachine()
export function ChatApp() {
// Obter ticketId e ticketRef da URL
const params = new URLSearchParams(window.location.search)
const ticketId = params.get("ticketId")
const ticketRef = params.get("ticketRef")
const isHub = params.get("hub") === "true"
// Aguardar cliente Convex estar pronto
if (!isReady || !client) {
if (error) {
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">
<span className="text-sm font-medium">Erro: {error}</span>
</div>
</div>
)
}
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-slate-200 px-4 py-2 text-slate-600 shadow-lg">
<Loader2 className="size-4 animate-spin" />
<span className="text-sm font-medium">Conectando...</span>
</div>
</div>
)
}
// Modo hub - lista de todas as sessoes
if (isHub || !ticketId) {
return <ChatHubWidget />
return (
<ConvexProvider client={client}>
<ChatHubWidget />
</ConvexProvider>
)
}
// Modo chat - conversa de um ticket especifico
return <ChatWidget ticketId={ticketId} ticketRef={ticketRef ? Number(ticketRef) : undefined} />
return (
<ConvexProvider client={client}>
<ChatWidget ticketId={ticketId} ticketRef={ticketRef ? Number(ticketRef) : undefined} />
</ConvexProvider>
)
}
export function ChatApp() {
return (
<ConvexMachineProvider>
<ChatAppContent />
</ConvexMachineProvider>
)
}
export { ChatWidget }