fix(desktop-chat): estabiliza janelas e melhora multi-conversas
All checks were successful
All checks were successful
This commit is contained in:
parent
380b2e44e9
commit
3f9461a18f
3 changed files with 134 additions and 84 deletions
|
|
@ -12,9 +12,9 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
|||
import { open as openDialog } from "@tauri-apps/plugin-dialog"
|
||||
import { openUrl as openExternal } from "@tauri-apps/plugin-opener"
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { Send, X, Loader2, MessageCircle, Paperclip, FileText, Image as ImageIcon, File, User, ChevronUp, Minimize2, Eye, Download, Check } from "lucide-react"
|
||||
import { Send, X, Loader2, MessageCircle, Paperclip, FileText, Image as ImageIcon, File, User, ChevronUp, Minimize2, Eye, Download, Check, MessagesSquare } from "lucide-react"
|
||||
import type { Id } from "@convex/_generated/dataModel"
|
||||
import { useMachineMessages, usePostMachineMessage, useMarkMachineMessagesRead, type MachineMessage } from "./useConvexMachineQueries"
|
||||
import { useMachineMessages, useMachineSessions, usePostMachineMessage, useMarkMachineMessagesRead, type MachineMessage } from "./useConvexMachineQueries"
|
||||
import { useConvexMachine } from "./ConvexMachineProvider"
|
||||
|
||||
const MAX_MESSAGES_IN_MEMORY = 200
|
||||
|
|
@ -178,7 +178,7 @@ function MessageAttachment({
|
|||
<button
|
||||
onClick={handleView}
|
||||
className="flex size-7 items-center justify-center rounded-full bg-white/20 hover:bg-white/30"
|
||||
title="Visualizar"
|
||||
aria-label="Visualizar anexo"
|
||||
>
|
||||
<Eye className="size-4 text-white" />
|
||||
</button>
|
||||
|
|
@ -186,7 +186,7 @@ function MessageAttachment({
|
|||
onClick={handleDownload}
|
||||
disabled={downloading}
|
||||
className="flex size-7 items-center justify-center rounded-full bg-white/20 hover:bg-white/30 disabled:opacity-60"
|
||||
title="Baixar"
|
||||
aria-label="Baixar anexo"
|
||||
>
|
||||
{downloading ? (
|
||||
<Loader2 className="size-4 animate-spin text-white" />
|
||||
|
|
@ -204,7 +204,11 @@ function MessageAttachment({
|
|||
return (
|
||||
<div className={`flex items-center gap-2 rounded-lg p-2 text-xs ${isAgent ? "bg-white/10" : "bg-slate-100"}`}>
|
||||
{getFileIcon(attachment.name)}
|
||||
<button onClick={handleView} className="flex-1 truncate text-left hover:underline" title="Visualizar">
|
||||
<button
|
||||
onClick={handleView}
|
||||
className="flex-1 truncate text-left hover:underline"
|
||||
aria-label={`Visualizar anexo ${attachment.name}`}
|
||||
>
|
||||
{attachment.name}
|
||||
</button>
|
||||
{sizeLabel && <span className="text-xs opacity-60">({sizeLabel})</span>}
|
||||
|
|
@ -212,7 +216,7 @@ function MessageAttachment({
|
|||
<button
|
||||
onClick={handleView}
|
||||
className={`flex size-7 items-center justify-center rounded-md ${isAgent ? "hover:bg-white/10" : "hover:bg-slate-200"}`}
|
||||
title="Visualizar"
|
||||
aria-label="Visualizar anexo"
|
||||
>
|
||||
<Eye className="size-4" />
|
||||
</button>
|
||||
|
|
@ -220,7 +224,7 @@ function MessageAttachment({
|
|||
onClick={handleDownload}
|
||||
disabled={downloading}
|
||||
className={`flex size-7 items-center justify-center rounded-md disabled:opacity-60 ${isAgent ? "hover:bg-white/10" : "hover:bg-slate-200"}`}
|
||||
title="Baixar"
|
||||
aria-label="Baixar anexo"
|
||||
>
|
||||
{downloading ? (
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
|
|
@ -250,6 +254,7 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
|
|||
|
||||
// Convex hooks
|
||||
const { apiBaseUrl, machineToken } = useConvexMachine()
|
||||
const { sessions: machineSessions = [] } = useMachineSessions()
|
||||
const { messages: convexMessages, hasSession, unreadCount, isLoading } = useMachineMessages(
|
||||
ticketId as Id<"tickets">,
|
||||
{ limit: MAX_MESSAGES_IN_MEMORY }
|
||||
|
|
@ -276,6 +281,22 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
|
|||
const unreadAgentMessageIds = useMemo(() => getUnreadAgentMessageIds(messages, unreadCount), [messages, unreadCount])
|
||||
const firstUnreadAgentMessageId = unreadAgentMessageIds[0] ?? null
|
||||
|
||||
const otherUnreadCount = useMemo(() => {
|
||||
if (machineSessions.length <= 1) return 0
|
||||
return machineSessions.reduce((sum, session) => {
|
||||
return sum + (session.ticketId === ticketId ? 0 : session.unreadCount)
|
||||
}, 0)
|
||||
}, [machineSessions, ticketId])
|
||||
|
||||
const handleOpenHub = useCallback(async () => {
|
||||
try {
|
||||
await invoke("open_hub_window")
|
||||
await invoke("set_hub_minimized", { minimized: false })
|
||||
} catch (err) {
|
||||
console.error("Erro ao abrir hub:", err)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const updateIsAtBottom = useCallback(() => {
|
||||
const el = messagesContainerRef.current
|
||||
if (!el) return
|
||||
|
|
@ -562,7 +583,7 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
|
|||
<button
|
||||
onClick={handleClose}
|
||||
className="ml-1 rounded-full p-1 text-slate-600 hover:bg-slate-300/60"
|
||||
title="Fechar"
|
||||
aria-label="Fechar chat"
|
||||
>
|
||||
<X className="size-4" />
|
||||
</button>
|
||||
|
|
@ -621,17 +642,31 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
|
|||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
{machineSessions.length > 1 && (
|
||||
<button
|
||||
onClick={handleOpenHub}
|
||||
className="relative rounded-md p-1.5 text-slate-500 hover:bg-slate-100"
|
||||
aria-label="Abrir lista de chats"
|
||||
>
|
||||
<MessagesSquare className="size-4" />
|
||||
{otherUnreadCount > 0 && (
|
||||
<span className="absolute -right-1 -top-1 flex size-5 items-center justify-center rounded-full bg-red-500 text-[10px] font-bold text-white">
|
||||
{otherUnreadCount > 9 ? "9+" : otherUnreadCount}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={handleMinimize}
|
||||
className="rounded-md p-1.5 text-slate-500 hover:bg-slate-100"
|
||||
title="Minimizar"
|
||||
aria-label="Minimizar chat"
|
||||
>
|
||||
<Minimize2 className="size-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="rounded-md p-1.5 text-slate-500 hover:bg-slate-100"
|
||||
title="Fechar"
|
||||
aria-label="Fechar chat"
|
||||
>
|
||||
<X className="size-4" />
|
||||
</button>
|
||||
|
|
@ -772,6 +807,7 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
|
|||
<button
|
||||
onClick={() => handleRemoveAttachment(att.storageId)}
|
||||
className="ml-1 rounded p-0.5 text-slate-400 hover:bg-slate-200 hover:text-slate-600"
|
||||
aria-label={`Remover anexo ${att.name}`}
|
||||
>
|
||||
<X className="size-3" />
|
||||
</button>
|
||||
|
|
@ -792,7 +828,7 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
|
|||
onClick={handleAttach}
|
||||
disabled={isUploading || isSending}
|
||||
className="flex size-9 items-center justify-center rounded-lg text-slate-500 transition hover:bg-slate-100 hover:text-slate-700 disabled:opacity-50"
|
||||
title="Anexar arquivo"
|
||||
aria-label="Anexar arquivo"
|
||||
>
|
||||
{isUploading ? (
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
|
|
@ -804,6 +840,7 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
|
|||
onClick={handleSend}
|
||||
disabled={(!inputValue.trim() && pendingAttachments.length === 0) || isSending}
|
||||
className="flex size-9 items-center justify-center rounded-lg bg-black text-white transition hover:bg-black/90 disabled:opacity-50"
|
||||
aria-label="Enviar mensagem"
|
||||
>
|
||||
{isSending ? (
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue