From b6da8e8474b7a1d0b951b25f4936f039197ff84b Mon Sep 17 00:00:00 2001
From: Fuwn <contact@fuwn.net>
Date: Tue, 2 Jun 2026 00:09:50 +0000
Subject: fix(updates): isolate feed failures so one feed can't break the page
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

onMount fetched the novel feed then the manga feed, each via
`(await fetch(...)).json()` with no error handling. When a feed endpoint
failed (e.g. all-novels, when wlnupdates returns HTML), `.json()` threw
and aborted onMount before the other feed loaded, leaving the whole page
stuck on skeletons. Load each feed through a fetchJson helper that
returns null on any failure (non-ok, network, or parse error) and set
the feed to null — the existing "Failed to load feed" state — when the
response is missing or malformed. A failing feed now degrades gracefully
while the other still renders.
---
 src/routes/updates/+page.svelte | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

(limited to 'src/routes/updates/+page.svelte')

diff --git a/src/routes/updates/+page.svelte b/src/routes/updates/+page.svelte
index 0534d0bb..bbcb087d 100644
--- a/src/routes/updates/+page.svelte
+++ b/src/routes/updates/+page.svelte
@@ -23,6 +23,7 @@ let novelFeed:
 				}[];
 			};
 	  }
+	| null
 	| undefined;
 let startTime: number;
 let mangaEndTime: number;
@@ -32,12 +33,22 @@ let directLink = browser
 	: false;
 let removeHeightObserver: (() => void) | undefined;
 
+const fetchJson = async (path: string) => {
+	try {
+		const response = await fetch(root(path));
+
+		return response.ok ? await response.json() : null;
+	} catch {
+		return null;
+	}
+};
+
 onMount(async () => {
 	removeHeightObserver = createHeightObserver(false);
 
 	startTime = performance.now();
 
-	const allNovels = await (await fetch(root("/api/updates/all-novels"))).json();
+	const allNovels = await fetchJson("/api/updates/all-novels");
 
 	if (allNovels?.data?.items)
 		for (const item of allNovels.data.items) {
@@ -45,11 +56,11 @@ onMount(async () => {
 			if (item.series) item.series.name = sanitizeFeedHtml(item.series.name);
 		}
 
-	novelFeed = allNovels;
+	novelFeed = allNovels?.data?.items ? allNovels : null;
 	novelEndTime = performance.now() - startTime;
 	startTime = performance.now();
 
-	const mangaFeed = await (await fetch(root("/api/updates/manga"))).json();
+	const mangaFeed = await fetchJson("/api/updates/manga");
 
 	if (mangaFeed?.items)
 		for (const item of mangaFeed.items) {
@@ -57,7 +68,7 @@ onMount(async () => {
 			item.content = sanitizeFeedHtml(item.content);
 		}
 
-	feed = mangaFeed;
+	feed = mangaFeed?.items ? mangaFeed : null;
 	mangaEndTime = performance.now() - startTime;
 });
 
-- 
cgit v1.2.3

