From 3ec72ca75b3de9652473c81de6b96e11554bcd5e Mon Sep 17 00:00:00 2001 From: emi Date: Sun, 26 Jul 2026 18:51:02 +0000 Subject: [PATCH] bookmarks: don't fabricate a local save when the server rejects it (#121) --- frontend/static/bookmarks-ui.js | 4 ++++ frontend/static/bookmarks.js | 23 ++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/frontend/static/bookmarks-ui.js b/frontend/static/bookmarks-ui.js index 82877ed..2f3b6a7 100644 --- a/frontend/static/bookmarks-ui.js +++ b/frontend/static/bookmarks-ui.js @@ -111,6 +111,10 @@ function buildStar() { try { if (existing) await bookmarks.remove(existing.id); else await bookmarks.add(loc.lat, loc.lon, currentLabel()); + } catch (e) { + // A real server rejection (e.g. the per-user cap) — bookmarks.add() no + // longer fakes a local save for this, so say why nothing changed. + alert(e.message || "Couldn't save this location."); } finally { btn.disabled = false; } diff --git a/frontend/static/bookmarks.js b/frontend/static/bookmarks.js index 361adfa..fd64a25 100644 --- a/frontend/static/bookmarks.js +++ b/frontend/static/bookmarks.js @@ -97,17 +97,34 @@ async function syncFromServer(preSyncLocal) { notify(); } -/** Save (or re-save with a new label) the given spot. Upserts either way. */ +/** Save (or re-save with a new label) the given spot. Upserts either way. + * + * Throws if signed in and the server explicitly rejects the request (e.g. the + * 409 per-user cap, a 422 on bad input) — that is a real failure, not the + * "offline" case below, and must not be papered over with a local-only + * bookmark that doesn't actually exist server-side and would just vanish, + * unexplained, on the next sync. A genuine network failure (no response at + * all) still falls through to the local-only save so the star visibly + * sticks even offline. */ export async function add(lat, lon, label) { const id = cellId(lat, lon); if (signedIn) { + let res = null; try { - const res = await apiFetch(uv("bookmarks"), { method: "POST", json: { lat, lon, label: label || undefined } }); + res = await apiFetch(uv("bookmarks"), { method: "POST", json: { lat, lon, label: label || undefined } }); + } catch (e) { + // offline / network failure: fall through to the local-only path below. + } + if (res) { if (res.ok) { const rows = await fetchServerList(); if (rows != null) { cache = rows.map(fromServerRow); writeLocal(cache); notify(); return find(lat, lon); } + } else { + let msg = `Couldn't save this location (${res.status}).`; + try { const d = await res.json(); if (typeof d.detail === "string") msg = d.detail; } catch (e) { /* no body */ } + throw new Error(msg); } - } catch (e) { /* fall through so the star still visibly sticks, even offline */ } + } } let b = cache.find((x) => cellId(x.lat, x.lon) === id); if (b) { if (label) b.label = label; }