fix(web): detail banner — a failed annotation fetch must not wipe its rows

fetchBannerAnnotations published the rebuilt rows unconditionally, with
a failed endpoint's list defaulted to empty — so one failing request
(a single handler 500, an auth blip on one call) silently wiped that
annotation kind from the banner while the other kind stayed, and the
empty state could claim "No notes or highlights on this book yet" over
real data. A full outage was already safe (the thrown fetch takes the
catch path, which keeps previous rows and toasts) — the partial case
was the hole, spotted while mirroring the app's fetch-wipe fix
(bookhoard-app 07bbb4c) onto the web.

Both responses must now succeed before the rows are published;
otherwise the previous rows stay and the next banner open retries
(bannerLoaded only sets on success). Verified on the emulator: banner
renders 3 rows, a full outage keeps them with bannerError recorded,
and a post-recovery refetch re-renders from live data.
This commit is contained in:
John O'Keefe committed 2026-10-10 13:56:32 -04:00
1 parent 361fdaab39
commit 8acaeddda5
1 file changed
+14 -2
+14 -2
View File
@@ -210,8 +210,20 @@ Alpine.data("bookDetail", () => {
headers: { Authorization: getAuthHeader() }, headers: { Authorization: getAuthHeader() },
}), }),
]); ]);
const notes = noteResp.ok ? await noteResp.json() : []; // A partial failure must not read as "no annotations": a failed
const highlights = hlResp.ok ? await hlResp.json() : []; // endpoint used to be published as an empty list, silently wiping
// that kind from the banner while the other kind stayed (found
// auditing the app's fetch-wipe fix, bookhoard-app 07bbb4c).
// Keep the previous rows; the next banner open retries.
if (!noteResp.ok || !hlResp.ok) {
console.warn(
`banner annotation load failed ` +
`(notes ${noteResp.status}, highlights ${hlResp.status}) — keeping previous`,
);
return;
}
const notes = await noteResp.json();
const highlights = await hlResp.json();
const built = buildAnnotationRows(notes, highlights); const built = buildAnnotationRows(notes, highlights);
this.bannerRows = built.rows; this.bannerRows = built.rows;
this.bannerSources = built.sources; this.bannerSources = built.sources;