Files
awoooi/apps/web/src/hooks/useIncidentStatusChains.ts
Your Name 1b28dcf3f9
All checks were successful
CD Pipeline / tests (push) Successful in 1m39s
Code Review / ai-code-review (push) Successful in 11s
CD Pipeline / build-and-deploy (push) Successful in 4m52s
CD Pipeline / post-deploy-checks (push) Successful in 3m18s
fix(web): speed up homepage live evidence loading
2026-05-29 10:28:37 +08:00

111 lines
3.4 KiB
TypeScript

'use client'
import { useEffect, useMemo, useState } from 'react'
import type { AwoooPStatusChain } from '@/components/awooop/status-chain'
import { API_V1_URL } from '@/lib/config'
interface UseIncidentStatusChainsOptions {
incidentIds: string[]
limit?: number
concurrency?: number
projectId?: string
refreshKey?: string | number | Date | null
timeoutMs?: number
}
interface UseIncidentStatusChainsResult {
statusChains: Record<string, AwoooPStatusChain | null>
requestedIncidentIds: string[]
isLoading: boolean
}
export function useIncidentStatusChains({
incidentIds,
limit = 25,
concurrency = 4,
projectId = 'awoooi',
refreshKey = null,
timeoutMs = 12000,
}: UseIncidentStatusChainsOptions): UseIncidentStatusChainsResult {
const incidentIdsKey = incidentIds.filter(Boolean).join('|')
const requestedIncidentIds = useMemo(() => {
return Array.from(new Set(incidentIdsKey ? incidentIdsKey.split('|') : [])).slice(0, limit)
}, [incidentIdsKey, limit])
const incidentKey = requestedIncidentIds.join('|')
const [statusChains, setStatusChains] = useState<Record<string, AwoooPStatusChain | null>>({})
const [isLoading, setIsLoading] = useState(false)
useEffect(() => {
if (requestedIncidentIds.length === 0) {
setStatusChains({})
setIsLoading(false)
return
}
let active = true
const controller = new AbortController()
const timeout = window.setTimeout(() => controller.abort(), timeoutMs)
const requestConcurrency = Math.max(1, Math.min(concurrency, requestedIncidentIds.length))
let nextIndex = 0
let activeRequests = 0
let completedRequests = 0
setIsLoading(true)
setStatusChains(Object.fromEntries(requestedIncidentIds.map(incidentId => [incidentId, null])))
const fetchStatusChain = async (incidentId: string): Promise<AwoooPStatusChain | null> => {
const params = new URLSearchParams({ project_id: projectId, incident_id: incidentId })
try {
const response = await fetch(`${API_V1_URL}/platform/status-chain?${params.toString()}`, {
cache: 'no-store',
signal: controller.signal,
})
if (!response.ok) return null
return await response.json() as AwoooPStatusChain
} catch {
return null
}
}
const finish = () => {
completedRequests += 1
if (completedRequests >= requestedIncidentIds.length) {
window.clearTimeout(timeout)
if (active) setIsLoading(false)
}
}
const pump = () => {
if (!active) return
while (activeRequests < requestConcurrency && nextIndex < requestedIncidentIds.length) {
const incidentId = requestedIncidentIds[nextIndex]
nextIndex += 1
activeRequests += 1
fetchStatusChain(incidentId)
.then(statusChain => {
if (active) {
setStatusChains(previous => ({ ...previous, [incidentId]: statusChain }))
}
})
.finally(() => {
activeRequests -= 1
finish()
pump()
})
}
}
pump()
return () => {
active = false
window.clearTimeout(timeout)
controller.abort()
}
}, [concurrency, incidentKey, projectId, requestedIncidentIds, refreshKey, timeoutMs])
return { statusChains, requestedIncidentIds, isLoading }
}
export type { UseIncidentStatusChainsOptions, UseIncidentStatusChainsResult }