import { useCallback, useEffect, useMemo, useState } from 'react' import { getLatestBoard, getLatestQueryDetail, getQueryPreview } from '../../api/workbench.js' import { isLiveRun, singletonMaps } from './model.js' const BOARD_POLL_MS = 5 * 60 * 1000 export function useQueryBoard() { const [preview, setPreview] = useState(null) const [board, setBoard] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState('') const [lastUpdatedAt, setLastUpdatedAt] = useState(null) const refreshBoard = useCallback(({ signal } = {}) => { return getLatestBoard({ signal }).then((payload) => { setBoard(payload) setLastUpdatedAt(new Date()) return payload }) }, []) useEffect(() => { const controller = new AbortController() setLoading(true) setError('') Promise.all([ getQueryPreview({ signal: controller.signal }), getLatestBoard({ signal: controller.signal }).catch(() => null), ]) .then(([previewPayload, boardPayload]) => { setPreview(previewPayload) setBoard(boardPayload) setLastUpdatedAt(new Date()) }) .catch((e) => { if (e.name !== 'AbortError') setError(e.message || '读取失败') }) .finally(() => setLoading(false)) return () => controller.abort() }, []) useEffect(() => { if (!isLiveRun(board?.run)) return undefined let stopped = false let timer = 0 let controller = null const tick = () => { controller = new AbortController() refreshBoard({ signal: controller.signal }) .catch(() => {}) .finally(() => { if (!stopped) timer = window.setTimeout(tick, BOARD_POLL_MS) }) } timer = window.setTimeout(tick, BOARD_POLL_MS) return () => { stopped = true window.clearTimeout(timer) controller?.abort() } }, [board?.run?.status, refreshBoard]) const latestByQuery = useMemo(() => singletonMaps(board), [board]) return { preview, board, latestByQuery, loading, error, lastUpdatedAt, refreshBoard, isPolling: isLiveRun(board?.run), } } export function useLatestQueryDetail(selectedQuery) { const [detail, setDetail] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState('') const load = useCallback(({ signal, background = false } = {}) => { if (!selectedQuery?.query_id) return Promise.resolve(null) if (!background) setLoading(true) setError('') return getLatestQueryDetail(selectedQuery.query_id, { signal }) .then((payload) => { setDetail(payload) return payload }) .catch((e) => { if (e.name !== 'AbortError') setError(e.message || '搜索结果读取失败') return null }) .finally(() => { if (!background) setLoading(false) }) }, [selectedQuery?.query_id]) useEffect(() => { setDetail(null) if (!selectedQuery?.query_id) return undefined const controller = new AbortController() load({ signal: controller.signal }) return () => controller.abort() }, [selectedQuery?.query_id, load]) return { detail, loading, error, reload: load } }