Design-Doc: Mitteilungen, Vorrats-Erlebnis & Live Activities
Datum: 07.06.2026 abends · Anlass: Gerätetest-Welle 1, Punkte #20, #21, #23, #24, #25 Status: Entwurf zur Diskussion — noch nichts implementiert. Mockups sind klickbar gedachte Zielbilder im echten Silverscale-Look.
1. 🔔 Notification-Center (#21)
Problem: Die App tut viel asynchron (Bon-Import 07:45, HF-Sync, KI-Jobs, eGYM/Garmin-Sync, nachts ai-resolve) — aber das Wissen darüber ist verstreut: Toasts verpuffen, die KI-Pill zeigt nur „läuft", Sync-Fehler sieht man nur zufällig. In der iPhone-App gibt es zudem (noch) gar keine Pushes — beim Öffnen fehlt das „Was ist seit gestern passiert?".
Konzept: Eine Glocke im Header (Badge = ungelesen), dahinter ein Sheet mit drei Zonen:
Mockup: Glocke mit Badge · „Läuft gerade" (live, mit Fortschritt) · Heute/Gestern mit Deep-Links und Ungelesen-Punkten.
Verhalten: - „Läuft gerade" ersetzt perspektivisch die schwebende KI-Pill: alle aktiven Jobs (KI-Recherche x/y, Foto-Schätzung, HF-Sync, eGYM/Garmin) mit Sonar + Fortschritt, live über den bestehenden Status-Poller (kein neuer Polling-Kanal!). - Einträge sind actionable: „2 ohne EAN → jetzt scannen" springt direkt in den Scan, „neu verbinden" in die Einstellungen. Jeder Eintrag = eine Zeile, Icon nach Art, relative Zeit. - Gelesen-Logik: Öffnen markiert nichts; Schließen markiert alles Sichtbare als gelesen (bewusstes „zur Kenntnis genommen"). Badge zählt nur Ungelesenes, max „9+". - Fehler bleiben oben: Ein roter Eintrag (Sync-Fehler, KI-Fehlschlag) bleibt ungelesen-stickig, bis seine Aktion ausgeführt oder er explizit weggewischt wurde. - Wir-Modus: Einträge sind pro Haushalt, nicht pro Profil (Bon-Import betrifft beide); profilspezifische Dinge (z. B. „dein Garmin-Login") tragen den Profil-Emoji.
Technik-Skizze:
- Tabelle notifications (id, user_id NULL=alle, kind, title, body, icon, link, dedupe_key, created_at, read_at). Erzeuger: importer.py, HF-Sync, ai_jobs.py, eGYM/Garmin-Worker — alle schreiben heute schon Logs, sie bekommen nur einen notify()-Helper dazu.
- API: GET /api/notifications, POST /api/notifications/read (alle/IDs). Badge-Zahl fährt im bestehenden /api/ai-resolve/status-Poller mit.
- Web-Push (PWA) bleibt unverändert der Out-of-App-Kanal; das Center ist die In-App-Wahrheit für beide Plattformen — und für die App (ohne Push) der einzige.
Aufwand: Backend ~½ Tag · Frontend ~1 Tag. Empfehlung: bauen — höchster Alltagswert pro Aufwand in dieser Liste.
2. 🏝️ Live Activities & Dynamic Island (#20)
Erst die ehrliche Machbarkeit, dann die Ideen:
| Voraussetzung | Stand bei uns |
|---|---|
| ActivityKit braucht eine WidgetKit-Extension (natives SwiftUI, eigenes Bundle) | Capacitor-Hülle hat keine; Community-Plugins existieren, aber die UI der Activity ist immer nativ zu bauen |
| Updates im Hintergrund brauchen APNs-Push | Gratis-Apple-ID/SideStore: keine Push-Entitlements → Activities aktualisieren nur, solange die App (kurz) läuft |
| SideStore-Resigning | Extensions verkomplizieren das Re-Signing und zählen gegen das App-ID-Limit der Gratis-ID |
Was trotzdem ginge (lokal gestartete Activities, Update nur bei App-Nutzung):
Zielbild: Tages-Budget als Live Activity — Ring, kcal übrig, nächste geplante Mahlzeit.
- Tagesbudget-Activity (Bild): startet beim ersten Log des Tages, aktualisiert bei jedem App-Besuch, endet 23:59. Auch ohne Push sinnvoll, weil man die App beim Loggen ohnehin öffnet — die Island zeigt danach den Stand.
- Einkaufs-Activity: Liste geöffnet → „7 von 12 ✓" in der Island, perfekt beim Einkaufen (App ist dabei aktiv im Wechsel).
- Job-Fortschritt (KI/Sync): kurzlebig, App ist eh offen — technisch der einfachste Fall.
Empfehlung: Parken bis zur Entscheidung „Apple Developer Account (99 €/Jahr)" — damit kämen Push-Updates UND der SideStore-7-Tage-Schmerz verschwände gleich mit (TestFlight/Ad-hoc). Bis dahin als Quick-Win ohne Extension: Home-Screen-Quick-Actions (3D-Touch aufs Icon → „🍽 Essen loggen", „📷 Scannen", „🛒 Einkaufsliste") — statische UIApplicationShortcutItems in der Info.plist, ein kleiner IPA-Anhang ohne neue Risiken.
3. 🧺 „Was koch ich draus?" — Vorrat → Gericht (#23)
Deine Journey: Im Vorrat Zutaten einsammeln → KI schlägt Gerichte zu genau diesen Zutaten vor, mit Nährwerten und Preis — wie Kochideen, nur vom Vorrat aus gedacht.
Schritt 1: „🧺 Sammeln"-Modus in den Vorräten — Checkboxen statt Stepper, unten wächst der Korb mit.
Schritt 2: Vorschläge zu den gesammelten Zutaten — kcal/Makros, €/Portion, und ehrlich: was fehlt (→ Einkaufsliste).
Flow im Detail:
1. Vorräte-Seite bekommt einen „🧺 Sammeln"-Toggle: Zeilen werden checkbar, unten schwebt die Korb-Leiste („3 Zutaten · Was koch ich draus?").
2. Tap → Sheet (gleiche Mechanik wie 🤖 Koch-Ideen): optionales Wunsch-Feld („schnell", „low carb") + Vorschläge.
3. Jeder Vorschlag: kcal/Makros pro Portion, ~€/Portion (Preis-Engine vom Nachkoch-Preisvergleich, ADR-20, wiederverwendet), und der Abgleich „aus dem Korb ✓ / zusätzlich aus Vorrat ✓ / fehlt".
4. Aktionen: „Als Gericht übernehmen" (bestehender adoptProposal-Flow) · „Fehlendes auf die Einkaufsliste".
Technik: POST /api/ai/ideas bekommt optional food_ids[]; das Backend reichert den Prompt mit den Zutaten + deren letzten Preisen an und rechnet die €/Portion serverseitig nach (nicht der KI glauben). UI ist zu ~70 % Wiederverwendung (Kochideen-Sheet, Vorrats-Liste).
Aufwand: ~1 Tag. Empfehlung: bauen, direkt nach dem Teilverbrauch (Punkt 4) — der Korb zeigt dann auch angebrochene Mengen korrekt an.
4. 🪸 Teilverbrauch sichtbar machen (#24)
Problem: Du isst eine halbe Packung Hack — der Vorrat weiß das sogar (der 🪸-Abzug rechnet schon heute in Gramm), aber die Anzeige tut so, als gäbe es nur ganze Packungen. „1×" kann alles zwischen krümelig-leer und originalverschlossen heißen.
Mockup: Füllbalken pro Artikel, „angebrochen"-Badge, Restmenge in Gramm, Schnellwerte beim manuellen Abziehen (¼ · ½ · ganz · frei).
Konzept:
- Füllbalken unter jedem Artikel mit Packungsbezug: Restmenge / Packungsgröße (die Daten existieren: inventory.qty ist float, packaging_weight_g ist gepflegt). Grün = ok, Bernstein < 25 %.
- „angebrochen"-Badge sobald eine Packung nicht mehr voll ist; Anzeige „~250 g von 500 g" — die Tilde ist ehrlich, es sind Schätzwerte aus den Log-Abzügen.
- Stepper erweitert: „−" fragt bei packungs-bezogenen Artikeln nach: ¼ / ½ / ganz / freie Gramm — statt stillschweigend 1 ganze Einheit abzuziehen.
- Mehrere Packungen: erst die angebrochene leeren, dann volle anbrechen (eine offene Packung pro Artikel als Modell-Annahme — reicht für den Haushalt).
Aufwand: ½–1 Tag, kein Schema-Umbau (reine Darstellungs- + Abzugslogik). Empfehlung: als Erstes bauen — es ist der spürbarste Alltagsschmerz und Fundament für Punkt 3.
5. ✨ Verbrauchs-Animationen à la YNAB (#25)
Idee: Bei YNAB sieht man Geld fließen — Beträge wandern sichtbar in Kategorien. Übertragen: Werte fliegen dorthin, wo sie wirken.
Storyboard: Eintragen → kcal-Chip fliegt zum Tagesring, −g-Chip zum Vorrat (Balken sinkt) → Ring pulst, Zahl tickt animiert.
Wo es sich lohnt (und nur dort):
| Fluss | Animation |
|---|---|
| Essen loggen | +358 kcal-Chip fliegt vom Eintragen-Button in den Tagesring; Ring pulst kurz, kcal-Zahl zählt animiert runter (~300 ms Count) |
| 🪸-Vorratsabzug | parallel fliegt −250 g zum 🪸; in der Vorrats-Liste sinkt der Füllbalken sichtbar mit Delta-Label |
| Bon → Vorrat / Liste abhaken | Balken füllen sich grün auf — der Gegen-Fluss („Einkauf kommt an") |
| Aktivität buchen | +kcal in Kelp-Grün zum Budget — das Budget „wächst" fühlbar |
Dosierung (wichtig für den WAF): Genau diese vier Flüsse, nirgendwo sonst. Dauer ~450 ms, eine Kurve (cubic-bezier(.22,1,.36,1) wie überall), prefers-reduced-motion → nur Toast wie bisher. Keine Konfetti-Eskalation.
Technik: Ein wiederverwendbarer Helfer flyValue(fromEl, toEl, label, color) — fixed positionierter Chip, FLIP-Transform von Quelle zu Ziel, danach pulse-Klasse am Ziel + Count-up via rAF. ~1 Tag inklusive Einbau an den vier Stellen.
Diskussionspunkt: Soll der Tagesring runter zählen (übrig, wie jetzt) oder beim Fliegen kurz das Gegessen hochzählen? Mein Vorschlag: Chip zeigt +kcal (das, was passiert ist), Ring-Zahl bleibt „übrig" und tickt runter — konsistent mit dem Mental-Modell „Budget".
6. Priorisierung & nächste Schritte
| Prio | Was | Aufwand | Warum zuerst |
|---|---|---|---|
| 1 | Teilverbrauch sichtbar (#24) | ½–1 T | Spürbarster Alltagsschmerz, Fundament für #23 |
| 2 | Notification-Center (#21) | 1½ T | Asynchrone App wird erklärbar; App hat keinen Push-Ersatz |
| 3 | Vorrat → Gericht (#23) | 1 T | Baut auf 1 auf, hohe Koch-Alltags-Freude |
| 4 | Fly-Animationen (#25) | 1 T | Delight; greift in 1 (Balken) und Tagesring |
| 5 | Home-Screen-Quick-Actions | ¼ T (+IPA) | Mini-Win aus dem Live-Activity-Komplex |
| — | Live Activities (#20) | groß | Geparkt bis Dev-Account-Entscheidung (99 €/Jahr) |
7. Offene Fragen an dich
- Glocke: oben im Header (Mockup) — okay, dass der Platz neben den Avataren enger wird? Alternative: ins „Mehr"-Menü (schlechter auffindbar).
- Gelesen-Verhalten: „Schließen = gelesen" wie vorgeschlagen, oder lieber pro Eintrag antippen?
- Vorrats-Warnungen („fast leer, ihr esst das 3×/Woche") als Notification-Art gewünscht, oder erst mal nur Import/KI/Sync?
- Teilverbrauch: Sind Schätzwerte mit Tilde („~250 g") für dich/Jaqueline okay, oder stört Unschärfe mehr als sie hilft?
- Dev-Account: 99 €/Jahr würden Live Activities + echte Pushes + Ende des 7-Tage-Refreshs kaufen. Auf die Mittelfrist-Liste?
- Ring-Zählrichtung bei der Animation (siehe Diskussionspunkt in §5)?
Mockups: HTML/CSS im echten Token-Set, gerendert 07.06.2026 · Bezug: REPORT-Audit + ADR-41–43.