mirror of
https://github.com/zammad/zammad
synced 2026-05-24 09:48:36 +00:00
121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
// Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
|
|
|
|
import { watchPausable } from '@vueuse/core'
|
|
import { ref, type Ref, type ComputedRef, onScopeDispose, toRef, watch, toValue } from 'vue'
|
|
|
|
import type { QueryHandler } from '#shared/server/apollo/handler'
|
|
import { connected } from '#shared/server/connection.ts'
|
|
import type { OperationQueryResult } from '#shared/types/server/apollo/handler'
|
|
|
|
import type { OperationVariables } from '@apollo/client/core'
|
|
|
|
export type QueryPollingOptions = {
|
|
enabled?: boolean // Enable polling, default is true.
|
|
randomize?: boolean // Randomize the interval (1000 milliseconds). Useful to prevent request at the same time.
|
|
}
|
|
|
|
const activePollings = new Set<() => void>()
|
|
|
|
export const stopAllQueryPollings = () => {
|
|
activePollings.forEach((stop) => stop())
|
|
}
|
|
|
|
export const useQueryPolling = <
|
|
TResult extends OperationQueryResult = OperationQueryResult,
|
|
TVariables extends OperationVariables = OperationVariables,
|
|
>(
|
|
query: QueryHandler<TResult, TVariables>,
|
|
interval: number | Ref<number> | ComputedRef<number> | (() => number),
|
|
variables?:
|
|
| Ref<Partial<TVariables>>
|
|
| ComputedRef<Partial<TVariables>>
|
|
| (() => Partial<TVariables>),
|
|
options?:
|
|
| QueryPollingOptions
|
|
| Ref<QueryPollingOptions>
|
|
| ComputedRef<QueryPollingOptions>
|
|
| (() => QueryPollingOptions),
|
|
) => {
|
|
const isPolling = ref(false)
|
|
let pollTimer: ReturnType<typeof setTimeout>
|
|
|
|
// Only randomize up to +1000ms to avoid requests happening at the same time
|
|
const randomizeInterval = toValue(options)?.randomize ? Math.floor(Math.random() * 1000) : 0
|
|
|
|
const intervalRef = toRef(interval)
|
|
|
|
const stopPolling = () => {
|
|
if (!isPolling.value) return
|
|
|
|
clearTimeout(pollTimer)
|
|
isPolling.value = false
|
|
activePollings.delete(stopPolling)
|
|
}
|
|
|
|
const startPolling = () => {
|
|
if (isPolling.value || (toValue(options)?.enabled !== undefined && !toValue(options)?.enabled))
|
|
return
|
|
|
|
isPolling.value = true
|
|
activePollings.add(stopPolling)
|
|
|
|
const poll = async () => {
|
|
const pollVariables = typeof variables === 'function' ? variables() : variables?.value
|
|
await query.refetch(pollVariables as TVariables).catch(() => {})
|
|
|
|
// Only schedule next poll after current one completes
|
|
if (isPolling.value) {
|
|
pollTimer = setTimeout(poll, intervalRef.value + randomizeInterval)
|
|
}
|
|
}
|
|
|
|
// Schedule first poll after interval instead of running immediately
|
|
pollTimer = setTimeout(poll, intervalRef.value + randomizeInterval)
|
|
}
|
|
|
|
watch(
|
|
() => toValue(options)?.enabled,
|
|
(newValue) => {
|
|
if (newValue) {
|
|
startPolling()
|
|
return
|
|
}
|
|
|
|
stopPolling()
|
|
},
|
|
)
|
|
|
|
// When connection is lost or established again, start or stop polling accordingly.
|
|
const { resume: startConnectionWatch } = watchPausable(
|
|
connected,
|
|
(newValue) => {
|
|
if (!isPolling.value && toValue(options)?.enabled !== undefined && !toValue(options)?.enabled)
|
|
return
|
|
|
|
if (newValue && !isPolling.value) {
|
|
startPolling()
|
|
} else if (!newValue && isPolling.value) {
|
|
stopPolling()
|
|
}
|
|
},
|
|
{
|
|
initialState: 'paused',
|
|
},
|
|
)
|
|
|
|
// Start the connection watcher only after the polling was started at least once.
|
|
watch(isPolling, startConnectionWatch, {
|
|
once: true,
|
|
})
|
|
|
|
// Automatically stop polling when scope is disposed
|
|
onScopeDispose(() => {
|
|
stopPolling()
|
|
})
|
|
|
|
return {
|
|
isPolling,
|
|
startPolling,
|
|
stopPolling,
|
|
}
|
|
}
|