"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { errorMessage, getExecutionView, listScriptBuilds } from "@/lib/api"; import { isTerminalStatus, type BuildSummary, type ExecutionViewV8 } from "@/lib/types"; const RUN_QUERY_KEY = "run"; const LAST_RUN_STORAGE_KEY = "script-build:last-run"; export function initialBuildId(builds: BuildSummary[], search: string, storedId?: string | null) { const queryId = new URLSearchParams(search).get(RUN_QUERY_KEY)?.trim(); if (queryId) return queryId; const remembered = storedId?.trim(); if (remembered && builds.some((build) => build.id === remembered)) return remembered; return builds[0]?.id || ""; } export function useExecutionView() { const [builds, setBuilds] = useState([]); const [selectedId, setSelectedId] = useState(""); const [view, setView] = useState(null); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const generation = useRef(0); const loadView = useCallback(async (id: string, signal?: AbortSignal, quiet = false) => { const requestId = ++generation.current; quiet ? setRefreshing(true) : setLoading(true); try { if (!id) throw new Error("请输入真实 Run ID"); const data = await getExecutionView(id, signal); if (requestId !== generation.current) return; setView(data); setError(null); const loadedId = String(data.header.id); setSelectedId(loadedId); rememberBuildId(loadedId); } catch (requestError) { if (signal?.aborted || requestId !== generation.current) return; if (quiet && view) { setError(`刷新失败,保留 ${view.capturedAt} 的快照:${errorMessage(requestError)}`); } else { setView(null); setError(`无法读取构建 #${id}:${errorMessage(requestError)}`); } } finally { if (requestId === generation.current) { setLoading(false); setRefreshing(false); } } }, [view]); useEffect(() => { const controller = new AbortController(); void listScriptBuilds(controller.signal) .then((items) => { setBuilds(items); const preferred = initialBuildId(items, window.location.search, readRememberedBuildId()); setSelectedId(preferred); if (!preferred) throw new Error("当前没有可读取的真实构建记录"); return loadView(preferred, controller.signal); }) .catch((listError) => { if (!controller.signal.aborted) { setView(null); setLoading(false); setError(`无法读取真实构建列表:${errorMessage(listError)}`); } }); return () => controller.abort(); }, []); useEffect(() => { if (!view || isTerminalStatus(view.header.status)) return; const timer = window.setTimeout(() => void loadView(String(view.header.id), undefined, true), 3000); return () => window.clearTimeout(timer); }, [view?.capturedAt, view?.header.id, view?.header.status, loadView]); const selectBuild = useCallback((id: string) => { const normalized = id.trim(); if (!normalized) return; setSelectedId(normalized); setView(null); setError(null); void loadView(normalized); }, [loadView]); return { builds, selectedId, view, loading, refreshing, error, selectBuild }; } function readRememberedBuildId() { try { return window.localStorage.getItem(LAST_RUN_STORAGE_KEY); } catch { return null; } } function rememberBuildId(id: string) { try { window.localStorage.setItem(LAST_RUN_STORAGE_KEY, id); } catch { // URL persistence still works when storage is unavailable. } const url = new URL(window.location.href); if (url.searchParams.get(RUN_QUERY_KEY) === id) return; url.searchParams.set(RUN_QUERY_KEY, id); window.history.replaceState(window.history.state, "", url); }