import { useEffect, useRef, useState } from "react"; const TICK_INTERVAL_MS = 120;const FADE_OUT_DELAY_MS = 220;const SEED_PERCENT = 4;const ASYMPTOTE_PERCENT = 90;const APPROACH_FACTOR = 0.08;const MIN_INCREMENT = 0.5; /** * Indeterminate progress simulator for the preview chrome's loading bar. * Animates 0 → 90% asymptotically while `loading` is true, snaps to 100% * on release, then resets after a short pause. * * Uses a ref to thread the latest progress through interval ticks without * needing `loading` to retrigger the effect, which sidesteps the stale- * closure pitfalls of reading `progress` directly. */export function useLoadingProgress(loading: boolean): number { const [progress, setProgress] = useState(0); const progressRef = useRef(0); progressRef.current = progress; useEffect(() => { if (!loading) { if (progressRef.current === 0) return; setProgress(100); const timer = window.setTimeout(() => setProgress(0), FADE_OUT_DELAY_MS); return () => window.clearTimeout(timer); } setProgress((value) => (value > 0 && value < 95 ? value : SEED_PERCENT)); const interval = window.setInterval(() => { const current = progressRef.current; if (current >= ASYMPTOTE_PERCENT) return; const remaining = ASYMPTOTE_PERCENT - current; const increment = Math.max(MIN_INCREMENT, remaining * APPROACH_FACTOR); setProgress(Math.min(ASYMPTOTE_PERCENT, current + increment)); }, TICK_INTERVAL_MS); return () => window.clearInterval(interval); }, [loading]); return progress;}
import { useEffect, useRef, useState } from "react";
const TICK_INTERVAL_MS = 120;
const FADE_OUT_DELAY_MS = 220;
const SEED_PERCENT = 4;
const ASYMPTOTE_PERCENT = 90;
const APPROACH_FACTOR = 0.08;
const MIN_INCREMENT = 0.5;
/**
* Indeterminate progress simulator for the preview chrome's loading bar.
* Animates 0 → 90% asymptotically while `loading` is true, snaps to 100%
* on release, then resets after a short pause.
*
* Uses a ref to thread the latest progress through interval ticks without
* needing `loading` to retrigger the effect, which sidesteps the stale-
* closure pitfalls of reading `progress` directly.
*/
export function useLoadingProgress(loading: boolean): number {
const [progress, setProgress] = useState(0);
const progressRef = useRef(0);
progressRef.current = progress;
useEffect(() => {
if (!loading) {
if (progressRef.current === 0) return;
setProgress(100);
const timer = window.setTimeout(() => setProgress(0), FADE_OUT_DELAY_MS);
return () => window.clearTimeout(timer);
}
setProgress((value) => (value > 0 && value < 95 ? value : SEED_PERCENT));
const interval = window.setInterval(() => {
const current = progressRef.current;
if (current >= ASYMPTOTE_PERCENT) return;
const remaining = ASYMPTOTE_PERCENT - current;
const increment = Math.max(MIN_INCREMENT, remaining * APPROACH_FACTOR);
setProgress(Math.min(ASYMPTOTE_PERCENT, current + increment));
}, TICK_INTERVAL_MS);
return () => window.clearInterval(interval);
}, [loading]);
return progress;
}