"use client"; import { useCallback, useEffect, useRef, useState } from "react"; export function useProgressiveFetch(loader: () => Promise, options: { timeoutMs?: number; immediate?: boolean } = {}) { const [data, setData] = useState(null); const [loading, setLoading] = useState(Boolean(options.immediate)); const [error, setError] = useState(""); const [updatedAt, setUpdatedAt] = useState(null); const abortRef = useRef(false); const run = useCallback(async () => { abortRef.current = false; setLoading(true); setError(""); let timeout: ReturnType | null = null; try { const timeoutPromise = new Promise((_, reject) => { timeout = setTimeout(() => reject(new Error("Request timed out")), options.timeoutMs ?? 12000); }); const value = await Promise.race([loader(), timeoutPromise]); if (!abortRef.current) { setData(value); setUpdatedAt(new Date().toISOString()); } } catch (err) { if (!abortRef.current) setError(err instanceof Error ? err.message : String(err)); } finally { if (timeout) clearTimeout(timeout); if (!abortRef.current) setLoading(false); } }, [loader, options.timeoutMs]); useEffect(() => { if (options.immediate) run(); return () => { abortRef.current = true; }; }, [options.immediate, run]); return { data, loading, error, updatedAt, retry: run }; } export function useVisibilityFetch(loader: () => Promise, enabled: boolean, options: { timeoutMs?: number } = {}) { return useProgressiveFetch(loader, { ...options, immediate: enabled }); }