Skip to content

Commit dfd071e

Browse files
committed
Enhance useSandbox hook with timeout handling for fetch requests and implement polling mechanism for tree and tab refresh. Add visibility change listener to trigger tree fetch when document becomes visible.
1 parent 39c6bab commit dfd071e

1 file changed

Lines changed: 37 additions & 14 deletions

File tree

web/src/lib/use-sandbox.ts

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,19 @@ export function useSandbox(isActive = false) {
7474
useEffect(() => { tabsRef.current = tabs; }, [tabs]);
7575

7676
const fetchTree = useCallback(async () => {
77+
const controller = new AbortController();
78+
const timeout = setTimeout(() => controller.abort(), 5000);
7779
try {
78-
const res = await fetch(`${API_BASE}/sandbox/tree`);
80+
const res = await fetch(`${API_BASE}/sandbox/tree`, {
81+
signal: controller.signal,
82+
});
7983
if (!res.ok) return;
8084
const data = await res.json();
8185
setTree(data);
8286
} catch {
83-
// silently fail -- sandbox may not exist yet
87+
// silently fail -- sandbox may not exist yet, or request timed out
88+
} finally {
89+
clearTimeout(timeout);
8490
}
8591
}, []);
8692

@@ -371,8 +377,13 @@ export function useSandbox(isActive = false) {
371377
const name = tab.path.split("/").pop() ?? "";
372378
const cat = fileCategory(name);
373379
if (cat === "image" || cat === "pdf") continue;
380+
const controller = new AbortController();
381+
const timeout = setTimeout(() => controller.abort(), 5000);
374382
try {
375-
const res = await fetch(`${API_BASE}/sandbox/file?path=${encodeURIComponent(tab.path)}`);
383+
const res = await fetch(
384+
`${API_BASE}/sandbox/file?path=${encodeURIComponent(tab.path)}`,
385+
{ signal: controller.signal },
386+
);
376387
const content = res.ok
377388
? await res.text()
378389
: `[Error: ${res.status} ${res.statusText}]`;
@@ -383,24 +394,36 @@ export function useSandbox(isActive = false) {
383394
});
384395
} catch {
385396
// silently skip tabs that fail to refresh
397+
} finally {
398+
clearTimeout(timeout);
386399
}
387400
}
388401
}, []);
389402

390403
useEffect(() => {
391-
fetchTree();
392-
if (isActive) {
393-
const interval = setInterval(() => {
394-
fetchTree();
395-
refreshOpenTabs();
396-
}, 2000);
397-
return () => clearInterval(interval);
398-
} else {
399-
const interval = setInterval(fetchTree, 3000);
400-
return () => clearInterval(interval);
401-
}
404+
let cancelled = false;
405+
406+
const poll = async () => {
407+
if (cancelled) return;
408+
await fetchTree();
409+
if (isActive && !cancelled) await refreshOpenTabs();
410+
if (!cancelled) {
411+
setTimeout(poll, isActive ? 1500 : 3000);
412+
}
413+
};
414+
415+
poll();
416+
return () => { cancelled = true; };
402417
}, [isActive, fetchTree, refreshOpenTabs]);
403418

419+
useEffect(() => {
420+
const onVisible = () => {
421+
if (document.visibilityState === "visible") fetchTree();
422+
};
423+
document.addEventListener("visibilitychange", onVisible);
424+
return () => document.removeEventListener("visibilitychange", onVisible);
425+
}, [fetchTree]);
426+
404427
return {
405428
tree,
406429
tabs,

0 commit comments

Comments
 (0)