From b4391998364aa851074ae1e9ec8f2c47a19fd4ec Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 4 Dec 2025 22:24:28 +0000 Subject: [PATCH] fix: Cancel pending story fetches on filter change to prevent UI jumps Co-authored-by: aider (gemini/gemini-2.5-pro) --- webclient/src/Feed.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/webclient/src/Feed.js b/webclient/src/Feed.js index 9d3dc60..116b40e 100644 --- a/webclient/src/Feed.js +++ b/webclient/src/Feed.js @@ -18,7 +18,9 @@ function Feed({ updateCache }) { }; useEffect(() => { - fetch(filterSmallweb ? '/api?smallweb=true' : '/api') + const controller = new AbortController(); + + fetch(filterSmallweb ? '/api?smallweb=true' : '/api', { signal: controller.signal }) .then(res => { if (!res.ok) { throw new Error(`Server responded with ${res.status} ${res.statusText}`); @@ -45,10 +47,13 @@ function Feed({ updateCache }) { let preloadedCount = 0; for (const [index, newStory] of newApiStories.entries()) { + if (controller.signal.aborted) { + break; + } try { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 10000); // 10-second timeout - const storyRes = await fetch('/api/' + newStory.id, { signal: controller.signal }); + const storyFetchController = new AbortController(); + const timeoutId = setTimeout(() => storyFetchController.abort(), 10000); // 10-second timeout + const storyRes = await fetch('/api/' + newStory.id, { signal: storyFetchController.signal }); clearTimeout(timeoutId); if (!storyRes.ok) { @@ -97,10 +102,16 @@ function Feed({ updateCache }) { setLoadingStatus(null); }, (error) => { + if (error.name === 'AbortError') { + console.log('Feed fetch aborted.'); + return; + } const errorMessage = `Failed to fetch the main story list from the API. Your connection may be down or the server might be experiencing issues. ${error.toString()}.`; setError(errorMessage); } ); + + return () => controller.abort(); }, [updateCache, filterSmallweb]); return (