desktop/chat: fallback de polling se WS falhar
This commit is contained in:
parent
fe361ff4d8
commit
e08dc21003
1 changed files with 136 additions and 6 deletions
|
|
@ -34,6 +34,11 @@ type MachineUpdatePayload = {
|
||||||
totalUnread: number
|
totalUnread: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MessagesPayload = {
|
||||||
|
messages: ChatMessage[]
|
||||||
|
hasSession: boolean
|
||||||
|
}
|
||||||
|
|
||||||
const FN_CHECK_UPDATES = "liveChat.checkMachineUpdates" as const
|
const FN_CHECK_UPDATES = "liveChat.checkMachineUpdates" as const
|
||||||
const FN_LIST_MESSAGES = "liveChat.listMachineMessages" as const
|
const FN_LIST_MESSAGES = "liveChat.listMachineMessages" as const
|
||||||
const FN_POST_MESSAGE = "liveChat.postMachineMessage" as const
|
const FN_POST_MESSAGE = "liveChat.postMachineMessage" as const
|
||||||
|
|
@ -92,13 +97,71 @@ 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) => callback(value),
|
(value) => {
|
||||||
onError
|
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(
|
export async function subscribeMachineMessages(
|
||||||
|
|
@ -107,16 +170,83 @@ 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.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(
|
const sub = client.onUpdate(
|
||||||
FN_LIST_MESSAGES as unknown as FunctionReference<"query">,
|
FN_LIST_MESSAGES as unknown as FunctionReference<"query">,
|
||||||
{
|
{
|
||||||
machineToken: token,
|
machineToken: token,
|
||||||
ticketId,
|
ticketId,
|
||||||
},
|
},
|
||||||
(value) => callback(value),
|
(value) => {
|
||||||
onError
|
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: {
|
export async function sendMachineMessage(input: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue