Use only WS for machine chat subscriptions

This commit is contained in:
rever-tecnologia 2025-12-09 14:56:53 -03:00
parent d2108ce16b
commit 988bf25010
2 changed files with 26 additions and 127 deletions

View file

@ -98,69 +98,15 @@ export async function subscribeMachineUpdates(
onError?: (error: Error) => void onError?: (error: Error) => void
): Promise<() => void> { ): Promise<() => void> {
const { client, token } = await ensureClient() const { client, token } = await ensureClient()
let stopped = false
let pollTimer: ReturnType<typeof setInterval> | null = null
const stopPoll = () => {
if (pollTimer) {
clearInterval(pollTimer)
pollTimer = null
}
}
const startPoll = async () => {
stopPoll()
try {
const { apiBaseUrl } = await getMachineStoreConfig()
const poll = async () => {
try {
const res = await fetch(`${apiBaseUrl}/api/machines/chat/poll`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ machineToken: token }),
})
if (!res.ok) throw new Error(`poll failed: ${res.status}`)
const data = (await res.json()) as MachineUpdatePayload
if (!stopped) callback(data)
} catch (err) {
if (!stopped) onError?.(err as Error)
}
}
await poll()
pollTimer = setInterval(poll, 4000)
} catch (err) {
onError?.(err as Error)
}
}
const sub = client.onUpdate( const sub = client.onUpdate(
FN_CHECK_UPDATES as unknown as FunctionReference<"query">, FN_CHECK_UPDATES as unknown as FunctionReference<"query">,
{ machineToken: token }, { machineToken: token },
(value) => { (value) => callback(value),
stopPoll() (err) => onError?.(err)
callback(value)
},
(err) => {
onError?.(err)
startPoll().catch(() => {
// erro já reportado via onError
})
}
) )
// fallback caso a inscrição falhe imediatamente
setTimeout(() => {
if (stopped) return
// Se não houver mensagens recebidas pelo WS em 3s, inicia polling
// (o callback do WS encerra o polling caso volte a funcionar)
startPoll().catch(() => {
// erro já reportado via onError
})
}, 3000)
return () => { return () => {
stopped = true
stopPoll()
sub.unsubscribe() sub.unsubscribe()
} }
} }
@ -171,53 +117,6 @@ export async function subscribeMachineMessages(
onError?: (error: Error) => void onError?: (error: Error) => void
): Promise<() => void> { ): Promise<() => void> {
const { client, token } = await ensureClient() const { client, token } = await ensureClient()
let stopped = false
let pollTimer: ReturnType<typeof setInterval> | null = null
let lastSince = 0
const stopPoll = () => {
if (pollTimer) {
clearInterval(pollTimer)
pollTimer = null
}
}
const startPoll = async () => {
stopPoll()
try {
const { apiBaseUrl } = await getMachineStoreConfig()
const poll = async () => {
try {
const res = await fetch(`${apiBaseUrl}/api/machines/chat/messages`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
action: "list",
machineToken: token,
ticketId,
since: lastSince || undefined,
}),
})
if (!res.ok) throw new Error(`messages poll failed: ${res.status}`)
const data = (await res.json()) as MessagesPayload
if (!stopped) {
callback(data)
const newest = (data.messages as ChatMessage[]).reduce(
(max: number, msg: ChatMessage) => Math.max(max, msg.createdAt),
lastSince
)
lastSince = newest
}
} catch (err) {
if (!stopped) onError?.(err as Error)
}
}
await poll()
pollTimer = setInterval(poll, 4000)
} catch (err) {
onError?.(err as Error)
}
}
const sub = client.onUpdate( const sub = client.onUpdate(
FN_LIST_MESSAGES as unknown as FunctionReference<"query">, FN_LIST_MESSAGES as unknown as FunctionReference<"query">,
@ -225,33 +124,11 @@ export async function subscribeMachineMessages(
machineToken: token, machineToken: token,
ticketId, ticketId,
}, },
(value) => { (value) => callback(value),
stopPoll() (err) => onError?.(err)
callback(value)
const newest = (value.messages as ChatMessage[]).reduce(
(max: number, msg: ChatMessage) => Math.max(max, msg.createdAt),
lastSince
)
lastSince = newest
},
(err) => {
onError?.(err)
startPoll().catch(() => {
// erro já reportado via onError
})
}
) )
setTimeout(() => {
if (stopped) return
startPoll().catch(() => {
// erro já reportado via onError
})
}, 3000)
return () => { return () => {
stopped = true
stopPoll()
sub.unsubscribe() sub.unsubscribe()
} }
} }

View file

@ -179,6 +179,28 @@ export function TicketChatPanel({ ticketId }: TicketChatPanelProps) {
toast.error(`Mensagem muito longa (max. ${MAX_MESSAGE_LENGTH} caracteres).`) toast.error(`Mensagem muito longa (max. ${MAX_MESSAGE_LENGTH} caracteres).`)
return return
} }
// Garantir que a sessão de chat ao vivo exista antes de enviar
if (liveChat?.hasMachine && !hasActiveSession) {
try {
await startLiveChat({
ticketId: ticketId as Id<"tickets">,
actorId: viewerId as Id<"users">,
})
} catch (error: unknown) {
const message = (() => {
if (error instanceof Error) {
const errorMsg = error.message.toLowerCase()
if (errorMsg.includes("offline")) return "Máquina offline. Aguarde a máquina ficar online para iniciar o chat."
if (errorMsg.includes("não encontrad") || errorMsg.includes("not found")) return "Máquina não encontrada"
}
return "Não foi possível iniciar o chat ao vivo."
})()
toast.error(message, { id: "live-chat" })
return
}
}
setIsSending(true) setIsSending(true)
try { try {
await postChatMessage({ await postChatMessage({