desktop/chat: fallback de polling se WS falhar

This commit is contained in:
esdrasrenan 2025-12-09 01:53:51 -03:00
parent fe361ff4d8
commit e08dc21003

View file

@ -34,6 +34,11 @@ type MachineUpdatePayload = {
totalUnread: number
}
type MessagesPayload = {
messages: ChatMessage[]
hasSession: boolean
}
const FN_CHECK_UPDATES = "liveChat.checkMachineUpdates" as const
const FN_LIST_MESSAGES = "liveChat.listMachineMessages" as const
const FN_POST_MESSAGE = "liveChat.postMachineMessage" as const
@ -92,13 +97,71 @@ export async function subscribeMachineUpdates(
onError?: (error: Error) => void
): Promise<() => void> {
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(
FN_CHECK_UPDATES as unknown as FunctionReference<"query">,
{ machineToken: token },
(value) => callback(value),
onError
(value) => {
stopPoll()
callback(value)
},
(err) => {
onError?.(err)
startPoll().catch(() => {
// erro já reportado via onError
})
}
)
return () => sub.unsubscribe()
// 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 () => {
stopped = true
stopPoll()
sub.unsubscribe()
}
}
export async function subscribeMachineMessages(
@ -107,16 +170,83 @@ export async function subscribeMachineMessages(
onError?: (error: Error) => void
): Promise<() => void> {
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.reduce((max, msg) => 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(
FN_LIST_MESSAGES as unknown as FunctionReference<"query">,
{
machineToken: token,
ticketId,
},
(value) => callback(value),
onError
(value) => {
stopPoll()
callback(value)
const newest = value.messages.reduce((max, msg) => Math.max(max, msg.createdAt), lastSince)
lastSince = newest
},
(err) => {
onError?.(err)
startPoll().catch(() => {
// erro já reportado via onError
})
}
)
return () => sub.unsubscribe()
setTimeout(() => {
if (stopped) return
startPoll().catch(() => {
// erro já reportado via onError
})
}, 3000)
return () => {
stopped = true
stopPoll()
sub.unsubscribe()
}
}
export async function sendMachineMessage(input: {