From 3906781c4e472f14eef03f0830eee47453e1e318 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Sat, 18 Apr 2026 16:54:03 +0200 Subject: [PATCH] feat(pwa): Schreib-Aktionen zeigen Offline-Toast statt stillem Fail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neuer Helper requireOnline(action) prüft vor jedem Schreib-Fetch den Online-Status. Offline: ein Toast erscheint ("Die Aktion braucht eine Internet-Verbindung."), Aktion bricht sauber ab. Der Button- State bleibt unverändert (kein optimistisches Update, das gleich wieder zurückgedreht werden müsste). Eingebaut in Rezept-Detail (8 Handler), Register (2), Wunschliste (2), Admin Domains/Profile/Backup, Home-Dismiss. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/lib/client/require-online.ts | 10 ++++++++++ src/routes/+page.svelte | 2 ++ src/routes/admin/domains/+page.svelte | 4 ++++ src/routes/admin/profiles/+page.svelte | 4 ++++ src/routes/recipes/+page.svelte | 3 +++ src/routes/recipes/[id]/+page.svelte | 9 +++++++++ src/routes/wishlist/+page.svelte | 3 +++ 7 files changed, 35 insertions(+) create mode 100644 src/lib/client/require-online.ts diff --git a/src/lib/client/require-online.ts b/src/lib/client/require-online.ts new file mode 100644 index 0000000..20e7c31 --- /dev/null +++ b/src/lib/client/require-online.ts @@ -0,0 +1,10 @@ +import { network } from './network.svelte'; +import { toastStore } from './toast.svelte'; + +// Soll vor jedem Schreib-Fetch aufgerufen werden. Liefert true wenn +// online (User darf weitermachen) oder false + Toast wenn offline. +export function requireOnline(action = 'Die Aktion'): boolean { + if (network.online) return true; + toastStore.error(`${action} braucht eine Internet-Verbindung.`); + return false; +} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index e75575a..504923e 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -10,6 +10,7 @@ import SearchFilter from '$lib/components/SearchFilter.svelte'; import { profileStore } from '$lib/client/profile.svelte'; import { searchFilterStore } from '$lib/client/search-filter.svelte'; + import { requireOnline } from '$lib/client/require-online'; const LOCAL_PAGE = 30; @@ -357,6 +358,7 @@ async function dismissFromRecent(recipeId: number, e: MouseEvent) { e.preventDefault(); e.stopPropagation(); + if (!requireOnline('Das Entfernen')) return; recent = recent.filter((r) => r.id !== recipeId); await fetch(`/api/recipes/${recipeId}`, { method: 'PATCH', diff --git a/src/routes/admin/domains/+page.svelte b/src/routes/admin/domains/+page.svelte index 6a5711e..77ad96e 100644 --- a/src/routes/admin/domains/+page.svelte +++ b/src/routes/admin/domains/+page.svelte @@ -3,6 +3,7 @@ import { Pencil, Check, X, Globe } from 'lucide-svelte'; import type { AllowedDomain } from '$lib/types'; import { confirmAction, alertAction } from '$lib/client/confirm.svelte'; + import { requireOnline } from '$lib/client/require-online'; let domains = $state([]); let loading = $state(true); @@ -25,6 +26,7 @@ async function add() { errored = null; if (!newDomain.trim()) return; + if (!requireOnline('Das Hinzufügen')) return; adding = true; const res = await fetch('/api/domains', { method: 'POST', @@ -59,6 +61,7 @@ async function saveEdit(d: AllowedDomain) { if (!editDomain.trim()) return; + if (!requireOnline('Das Speichern')) return; saving = true; try { const res = await fetch(`/api/domains/${d.id}`, { @@ -92,6 +95,7 @@ destructive: true }); if (!ok) return; + if (!requireOnline('Das Entfernen')) return; await fetch(`/api/domains/${d.id}`, { method: 'DELETE' }); await load(); } diff --git a/src/routes/admin/profiles/+page.svelte b/src/routes/admin/profiles/+page.svelte index 9a58807..52010aa 100644 --- a/src/routes/admin/profiles/+page.svelte +++ b/src/routes/admin/profiles/+page.svelte @@ -1,6 +1,7 @@