Dialy ist ein Alltagsbegleiter für Menschen mit Typ‑1‑ (und Typ‑2‑) Diabetes. Er hilft bei drei Dingen: Kohlenhydrate einer Mahlzeit schätzen, Sport und seine Wirkung auf den Blutzucker planen, und ein Tagebuch aus Mahlzeiten und Blutzuckerwerten führen.
Dialy ist kein Medizinprodukt. Die App berechnet keine Insulindosen und gibt keine Therapieempfehlungen. Sie dokumentiert nur selbst eingegebene Werte und schätzt Kohlenhydrate sowie einen illustrativen Blutzucker‑Trend.
Built with React Native + TypeScript on Expo (managed workflow). Runs on iOS and Android, fully offline-first, dark mode by default (optional light mode).
Because Dialy is a mobile app, you run it on a phone or an emulator/simulator — not in a browser. There are two ways to see it running:
| Method | Needs | Best for |
|---|---|---|
| A. Expo Go on a real phone | Just your phone + the Expo Go app | ✅ Fastest, works on any computer (Windows/Mac/Linux) |
| B. Emulator / Simulator | Android Studio (all OS) or Xcode (Mac only) | No phone needed; iOS Simulator only on macOS |
If you just want to see it quickly: use Method A with your phone. It takes about 2 minutes and is identical on Windows, macOS and Linux.
iOS Simulator requires a Mac. There is no way around this — Apple only ships the iOS Simulator with Xcode on macOS. On Windows/Linux, run iOS via Expo Go on a real iPhone (Method A) instead.
You need Node.js 18 or newer and Git. Check what you have:
node --version # should print v18.x or higher
git --versionIf Node is missing or too old, install it (per-OS steps below), then continue.
🪟 Windows
- Download the LTS installer from https://nodejs.org and run it
(accept the defaults). This installs both
nodeandnpm. - Install Git from https://git-scm.com/download/win (accept the defaults).
- Close and reopen your terminal (use PowerShell or Windows Terminal),
then verify:
node --version git --version
Tip: alternatively
winget install OpenJS.NodeJS.LTSandwinget install Git.Git.
🍎 macOS
Using Homebrew (recommended):
brew install node gitOr download the macOS installer from https://nodejs.org. Verify:
node --version
git --version🐧 Linux
Debian/Ubuntu:
# Node.js LTS via NodeSource
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs gitFedora:
sudo dnf install -y nodejs gitArch:
sudo pacman -S nodejs npm gitVerify:
node --version
git --versiongit clone https://github.com/olivierluethy/Dialy.git
cd Dialy
npm installnpm install downloads everything the app needs (~1–2 minutes). You only do
this once (and again whenever dependencies change).
npx expo startThis starts the Metro bundler and prints a QR code plus a menu of
shortcut keys. Leave this terminal running — it serves the JavaScript to
your device. Press Ctrl + C to stop it later.
Now pick Method A (real phone) or Method B (emulator/simulator) below.
This is the easiest path and works the same on every operating system.
- Install Expo Go on your phone:
- iPhone: App Store → search "Expo Go".
- Android: Google Play → search "Expo Go".
- Make sure your phone and computer are on the same Wi‑Fi network.
- With
npx expo startrunning, scan the QR code in the terminal:- iPhone: open the Camera app, point it at the QR code, tap the banner that appears → it opens in Expo Go.
- Android: open Expo Go → "Scan QR code" → scan it.
- The app downloads and launches. Edit a file and save → it hot-reloads on the phone automatically.
Wi‑Fi blocking the connection? Some home/office/guest networks block device‑to‑device traffic. Use a tunnel instead:
npx expo start --tunnel(The first time, it may ask to install @expo/ngrok — say yes.) Then scan the
QR code again.
-
Install Android Studio.
-
Open Android Studio → More Actions → Virtual Device Manager → Create Device → pick e.g. Pixel 7, choose a recent system image (download it), finish, then press ▶ to boot the emulator. Leave it running.
-
Make sure the Android SDK command-line tools are on your
PATH:🪟 Windows (PowerShell, set once)
setx ANDROID_HOME "$env:LOCALAPPDATA\Android\Sdk" # Then add these to PATH (Settings → Environment Variables), or: setx PATH "$env:PATH;$env:LOCALAPPDATA\Android\Sdk\platform-tools"
Reopen the terminal afterwards.
🍎 macOS / 🐧 Linux (add to ~/.zshrc or ~/.bashrc)
export ANDROID_HOME=$HOME/Android/Sdk # macOS: $HOME/Library/Android/sdk export PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator
Reload with
source ~/.bashrc(or~/.zshrc).Verify the emulator is detected:
adb devicesshould list it. -
With
npx expo startrunning, pressain that terminal. Expo installs Expo Go into the emulator and launches Dialy.
- Install Xcode from the Mac App Store (large download).
- Open Xcode once to finish component installation, then install the command
line tools:
xcode-select --install
- With
npx expo startrunning, pressi. Expo boots the iOS Simulator, installs Expo Go, and launches Dialy.
On Windows/Linux there is no iOS Simulator — use Method A with a real iPhone for iOS.
The Ratgeber and KH-Rechner work with no backend. Creating an account (needed for the Tagebuch / diary sync) needs a Supabase backend. The easiest, fully cross-platform way is to run Supabase locally in Docker — no cloud account, no keys to copy. It's the same three commands on every OS.
One-time: install Docker
| OS | How |
|---|---|
| 🪟 Windows | Install Docker Desktop, launch it, wait for "Engine running". |
| 🍎 macOS | Install Docker Desktop (or brew install --cask docker), launch it. |
| 🐧 Linux | curl -fsSL https://get.docker.com | sudo sh then sudo usermod -aG docker $USER and log out/in. |
Every time — start the backend, then the app:
npm install # first time only (also fetches the Supabase CLI locally)
npm run backend:start # boots local Supabase in Docker + writes .env automatically
npm start -- --clear # start Expo (‑‑clear so it picks up the new .env)That's it — open the app, tap Konto erstellen, and register. Local Supabase has email confirmation off, so you're signed in immediately (no verification email). Data now persists in Postgres and syncs.
Useful commands:
npm run backend:status # show local URLs + keys (Studio UI at http://localhost:54323)
npm run backend:stop # stop the containers (data is kept)
npm run backend:reset # re-apply migrations from scratch (wipes local data)📱 Using a real phone with the local backend? The phone must reach your computer. Over USB (Android): run
npm run phone:link(forwards Metro and Supabase ports viaadb reverse). On the same Wi‑Fi you can instead setEXPO_PUBLIC_SUPABASE_URLin.envtohttp://<your-computer-ip>:54321. For web in the browser,localhostjust works — nothing extra needed.
☁️ Prefer the cloud (works anywhere, no Docker)? See Supabase einrichten below — create a free project and drop its URL + anon key into
.env.
🪟 Windows
# 1) install Node LTS + Git (see above), then:
git clone https://github.com/olivierluethy/Dialy.git
cd Dialy
npm install
npx expo start
# scan QR with Expo Go (iPhone/Android), or press "a" for Android emulator🍎 macOS
brew install node git
git clone https://github.com/olivierluethy/Dialy.git
cd Dialy
npm install
npx expo start
# press "i" (iOS Simulator) or "a" (Android emulator), or scan the QR code🐧 Linux
# install Node LTS + Git (see above), then:
git clone https://github.com/olivierluethy/Dialy.git
cd Dialy
npm install
npx expo start
# scan QR with Expo Go, or press "a" for Android emulator (no iOS Simulator on Linux)| Symptom | Fix |
|---|---|
| QR code won't connect / "Something went wrong" | Same Wi‑Fi? Try npx expo start --tunnel. |
command not found: npx / node |
Node isn't installed or terminal wasn't reopened after install. Re-check step 0. |
| Metro cache acting up after edits | Restart with npx expo start -c (clears the cache). |
Pressing a does nothing |
The Android emulator isn't running, or adb devices doesn't list it. Boot the AVD first; check ANDROID_HOME/PATH. |
Pressing i fails (Mac) |
Run xcode-select --install and open Xcode once to finish setup. |
| Port 8081 already in use | Stop the other Metro instance, or run npx expo start --port 8082. |
| Want to verify it compiles without a device | npm run typecheck (type check) and npx expo-doctor (health check). |
Web: blank/white page, console shows MIME type ("application/json") mismatch |
The web bundle failed to build and Metro returned a JSON error. Read the terminal / the JSON body for the real cause. Known ones are already handled in metro.config.js (see Web-Unterstützung below); if new, restart with npx expo start -c. |
Web: white page, console shows Cannot find native module 'ExpoSQLite' |
expo-sqlite has no web build. Handled by src/db/database.web.ts (sql.js). Make sure it exists and that npm install ran. |
Web: white page with a requireNativeComponent / BVLinearGradient error |
A bare native module leaked into the web bundle. react-native-linear-gradient is aliased to expo-linear-gradient in metro.config.js — restart with -c. |
- React Native + TypeScript über Expo (managed workflow)
- React Navigation v7 — Bottom-Tabs (5) + Stacks
- Zustand — globaler State (Diabetes-Typ, Session, Premium-Flag)
- react-native-gifted-charts — Blutzucker-Verlaufschart (siehe Hinweis unten)
- expo-sqlite mit dünner Repository-Schicht — lokale Datenbank (siehe Hinweis)
- Supabase (PostgreSQL, Auth, Auto-REST) — Sync-Ziel
- Supabase Edge Functions — Sync-Trigger (gestubbt)
- Firebase Cloud Messaging, HealthKit/Health Connect, Sentry — hinter Feature-Flags
- Chart-Bibliothek:
react-native-gifted-chartsstatt Victory Native XL. Gifted-charts baut aufreact-native-svgauf und läuft sofort in Expo Go; Victory Native XL benötigt Skia und einen eigenen Dev-Build. - Lokale DB:
expo-sqlitestatt WatermelonDB. WatermelonDB benötigt ein natives Modul, das im managed Expo-Workflow (Expo Go) nicht ohne Eject baut. Das §7‑Datenmodell und der Sync-Vertrag sind in einer kleinen Repository-/Sync-Schicht darauf umgesetzt — der Vertrag hält unabhängig von der Storage-Engine.
Der KH-Rechner enthält rund 1'200 generische Lebensmittel aus der
Schweizer Nährwertdatenbank des BLV
(Bundesamt für Lebensmittelsicherheit und Veterinärwesen), zusätzlich zu den
handgepflegten Einträgen in src/data/seedFoods.ts (mit Portionsgrössen und
glykämischem Index).
- Übernommen werden die Kategorien (für den Kategorie-Filter im KH-Rechner;
Kurznamen in
src/data/foodCategories.ts) und pro 100 g: verfügbare Kohlenhydrate, Zucker, Fett. „Sp.“ (Spuren) und „<x“ werden zu 0; Lebensmittel mit unbekanntem Wert („k.A.“) werden ausgelassen statt geschätzt. - Die BLV-Daten enthalten keinen glykämischen Index (Anzeige „k.A.“) und keine Portionsgrössen (Eingabe in Gramm).
- Die Nutzung ist laut BLV kostenlos und auch in Ernährungs-Apps erlaubt, mit Quellenangabe – sie steht bei jedem BLV-Lebensmittel und in der Datenschutzerklärung.
Auf eine neue Version aktualisieren: Excel-Datei von https://naehrwertdaten.ch/de/downloads/ herunterladen, dann
npm run import:blv -- pfad/zu/Schweizer_Nahrwertdatenbank.xlsxDas schreibt src/data/blvFoods.json; die App übernimmt die neue Version beim
nächsten Start automatisch.
Zusätzlich rund 6'900 Lebensmittel aus dem Bundeslebensmittelschlüssel (BLS) 4.0 des Max Rubner-Instituts (Lizenz CC BY 4.0, Quellenangabe in der App), darunter ~2'000 Gerichte wie Döner Kebab, Falafel oder Frühlingsrollen. Werte pro 100 g; kein GI, keine Portionsgrössen.
Keine Duplikate: scripts/import-bls.mjs lässt einen BLS-Eintrag weg, wenn
es schon ein Lebensmittel gibt (BLV oder kuratiert) mit
- denselben Wörtern im Namen (Gross-/Kleinschreibung, Umlaute, Satzzeichen, Klammerzusätze, Reihenfolge und Einzahl/Mehrzahl egal) – auch bei anderen Werten, oder
- fast demselben Namen (≥ 75 % gleiche Wörter) bei einem Gericht oder bei ähnlichen Kohlenhydraten (± 5 g bzw. 30 %).
Der vorhandene Eintrag hat immer Vorrang. Jede Entscheidung steht in
data-sources/bls-duplicates-report.txt. Zusätzlich überspringt die App beim
Einspielen BLV-/BLS-Einträge, die genau wie ein kuratierter Eintrag heissen.
npm run import:bls -- data-sources/BLS_4_0_Daten_2025_DE.xlsxRund 680 der BLV-Lebensmittel haben eine „Übliche Portion“ (z. B. Teigwaren, gekocht: 170 g; Apfel: 207 g). Sie stammt aus der Nationalen Ernährungserhebung menuCH 2014-15 des BLV (Median der tatsächlich gegessenen Menge, pro Kategorie die Mahlzeit mit den meisten Beobachtungen).
- Die Zuordnung BLV-Lebensmittel → menuCH-Kategorie steht als Regeln in
scripts/import-menuch.mjs. Sie gilt nur für Lebensmittel im verzehrten Zustand (gekocht, zubereitet, roh essbar) – nie für trockene oder rohe Zutaten wie Mehl, Teigwaren trocken oder rohes Fleisch. Kräuter, Gewürze und Saucen erhalten keine Portion. - Quelle: opendata.swiss – menuCH Portionsgrössen, Datei „Portionsgrössen pro Mahlzeit“. Quellenangabe in der App.
Aktualisieren (Rohdateien gehören in den von Git ignorierten Ordner
data-sources/):
npm run import:blv -- data-sources/Schweizer_Nahrwertdatenbank.xlsx
npm run import:menuch -- data-sources/menuCH_portion_sizes_2014_2015_per_meal.xlsxRund 19'000 Markenprodukte aus der Schweiz (z. B. „Nutella“, „Cashewkerne (Alesto)“) stammen aus Open Food Facts – von Freiwilligen erfasst, deshalb streng gefiltert: nur Produkte mit deutschem Namen, deklarierten (nicht geschätzten) Kohlenhydraten, plausiblen Werten (Zucker ≤ KH, Energie passt zu den Nährwerten) und ohne Open-Food-Facts-Qualitätsfehler; nichts, was es in der App schon gibt. Die Portion „laut Packung“ wird übernommen. Im KH-Rechner: „Quelle: Open Food Facts (ODbL)“.
Lizenz: Open Database License (ODbL 1.0). src/data/offFoods.json ist
eine abgeleitete Datenbank und steht ebenfalls unter ODbL – Details in
data-sources/open-food-facts-LICENSE.md.
Aktualisieren: Dump openfoodfacts-products.jsonl.gz (~12 GB) von
https://world.openfoodfacts.org/data herunterladen (nicht entpacken), dann
npm run import:off -- pfad/zu/openfoodfacts-products.jsonl.gzDauert ca. 25 Minuten; Zahlen pro Filterregel und Beispiele stehen danach in
data-sources/off-import-report.txt.
Die App ist auf Deutsch, Französisch, Italienisch und Englisch verfügbar. Standard ist die Sprache des Geräts; umstellen lässt sie sich unter „Einstellungen“ (angemeldet) bzw. „Anmelden“ (Abschnitt „Sprache“).
- Oberfläche:
src/i18n/locales/de.tsist das Referenz-Wörterbuch,en.ts,fr.ts,it.tshaben (per TypeScript erzwungen) dieselbe Form. Komponenten holen die Texte mituseT(), Code ausserhalb von React mitgetT(). - Gespeicherte Daten bleiben deutsch (Lebensmittelnamen, Kategorien,
Portionsangaben – auch das, was der Dialy-Editor bearbeitet). Übersetzt wird
erst beim Anzeigen (
src/i18n/foods.ts); die Suche im KH-Rechner findet ein Lebensmittel in der gewählten Sprache und zusätzlich über den deutschen Namen. - Lebensmittelnamen stehen in
src/data/foodNames/{en,fr,it}.json:- BLV: offizielle englische, französische und italienische Ausgabe der Schweizer Nährwertdatenbank (naehrwertdaten.ch/en|fr|it/downloads/),
- BLS: offizielle englische Namen; Französisch/Italienisch aus
data-sources/bls-names-fr-it.tsv(aus dem Englischen übersetzt), - Open Food Facts: die Produktnamen
product_name_en/fr/it, sobaldimport:offneu gelaufen ist; bis dahin der deutsche Name, - eigene Lebensmittel und Markengetränke: Feld
namesinsrc/data/seedFoods.ts/brandDrinks.ts.
- Ratgeber-Artikel kommen aus Supabase und sind (noch) nur deutsch; in den anderen Sprachen zeigt die App einen Hinweis.
Aktualisieren (nach import:blv, import:bls oder import:off):
npm run import:namesDie App läuft nativ (iOS/Android via Expo Go) und im Browser
(npm run web bzw. npx expo start → Taste w). Drei native Bausteine haben
im Web keinen Gegenpart und würden sonst zu einer weissen Seite führen (der
Fehler passiert bei der Modul-Auswertung, bevor React etwas rendert). Alle
drei sind zentral in metro.config.js + einer plattform-spezifischen Datei
gelöst — der native Pfad bleibt unverändert:
| Problem im Web | Ursache | Lösung |
|---|---|---|
Bundle-Build bricht ab (@opentelemetry/api nicht auflösbar) |
@supabase/supabase-js macht im Browser-Build ein optionales import('@opentelemetry/api'); Metro versucht es statisch aufzulösen |
metro.config.js mappt das Modul auf ein leeres Modul ({ type: 'empty' }) — Supabase fängt das Fehlen selbst ab |
requireNativeComponent is not a function / BVLinearGradient |
react-native-gifted-charts zieht react-native-linear-gradient (bare native module) rein; dessen Web-Pfad ruft requireNativeComponent beim Import auf |
metro.config.js aliased react-native-linear-gradient → expo-linear-gradient (funktioniert auf Web + iOS + Android + Expo Go) |
Cannot find native module 'ExpoSQLite' |
expo-sqlite liefert in SDK 52 kein Web-Build |
src/db/database.web.ts — Metro wählt es im Web automatisch (.web.ts) und implementiert dieselbe async-API mit sql.js (SQLite als WebAssembly). Persistenz via IndexedDB; Fallback: In-Memory pro Session. Nativ bleibt database.ts (echtes expo-sqlite) die Quelle der Wahrheit. |
Nach Änderungen an metro.config.js immer mit npx expo start -c
(Cache leeren) neu starten.
Ohne Konfiguration läuft die App komplett offline (Auth & Sync deaktiviert; der Ratgeber bleibt leer, siehe unten). Beim ersten Start wird die lokale Datenbank mit den Lebensmitteln befüllt. Das Tagebuch startet leer: Einträge gehören immer zu einem Konto, und jedes Konto sieht nur seine eigenen – auch wenn sich mehrere Konten auf demselben Gerät anmelden.
Zum Aktivieren von Konto/Sync .env.example nach .env kopieren und ausfüllen:
cp .env.example .env| Variable | Zweck |
|---|---|
EXPO_PUBLIC_SUPABASE_URL |
Supabase Projekt-URL (Settings → API) |
EXPO_PUBLIC_SUPABASE_ANON_KEY |
Supabase anon-Key (öffentlich, client-sicher) |
EXPO_PUBLIC_SENTRY_DSN |
Sentry DSN (leer = No-op) |
EXPO_PUBLIC_CONTACT_EMAIL |
Ausweich-Adresse, die das Kontaktformular nennt, wenn eine Nachricht nicht gesendet werden kann (offline); leer = Platzhalter kontakt@dialy.example |
EXPO_PUBLIC_FEATURE_FCM |
true aktiviert FCM-Sync-Trigger (sonst Sync-on-Foreground) |
EXPO_PUBLIC_FEATURE_HEALTH |
true aktiviert die Health-Import-Schnittstelle |
⚠️ Niemals den Supabase Service-Role-Key oder DB-Zugangsdaten in den Client legen. Nur der anon-Key gehört hierher. Row-Level-Security (siehesupabase/migrations/0002_rls.sql) stellt sicher, dass jeder Nutzer nur seine eigenen Zeilen liest/schreibt.
💡 Für lokale Entwicklung ist Abschnitt 3C (Supabase lokal via Docker) meist der einfachste Weg — kein Cloud-Konto, keine Keys zum Kopieren. Der folgende Abschnitt beschreibt die Cloud-Variante.
# Schema + RLS anwenden (Supabase CLI)
supabase db push
# bzw. die SQL-Dateien der Reihe nach im SQL-Editor ausführen:
# supabase/migrations/0001_init.sql … 0005_food_categories.sqlSupabase erlaubt jede E-Mail-Adresse nur einmal. Für Tests:
- Mehrere Konten mit einem Postfach (Plus-Adressen): Gmail stellt alles
nach einem
+ins selbe Postfach zu. Für Supabase sind das verschiedene Adressen, die Mails landen aber alle bei dir:deinname+test1@gmail.comdeinname+test2@gmail.com
- Gleiche Adresse nochmals registrieren: das Konto vorher im Dashboard unter Authentication → Users löschen (Delete user).
- Einschränkung: Der eingebaute Mailversand von Supabase schickt Bestätigungs-Mails nur an Adressen der Projekt-Mitglieder und nur wenige pro Stunde. Für Plus-Adressen die Konten deshalb direkt im Dashboard anlegen: Authentication → Users → Add user → Create new user mit „Auto Confirm User“ – oder einen eigenen SMTP-Dienst einrichten.
- Damit der Bestätigungslink in der App landet, unter Authentication → URL
Configuration die Site URL auf
http://localhost:8081setzen (Web).
Die Artikel werden zentral in Supabase gepflegt (Table Editor → articles) und von der App live geladen – Änderungen erscheinen ohne App-Update. Sie werden nicht auf dem Gerät gespeichert: offline zeigt der Ratgeber keine Artikel, sondern einen Hinweis mit „Erneut versuchen“.
- Felder:
category(z. B. Sport, Ernährung),title,body(Absätze durch Leerzeilen getrennt),read_minutes,published_at(Datum),diabetes_type(t1,t2oderboth). Ausblenden:deleted_atsetzen. - Startbestand (11 Artikel mit Platzhaltertext):
data-sources/supabase-articles.sqleinmal im SQL Editor ausführen.
Dialy-Editor (einfacher als der Table Editor): npm run editor startet
ihn auf http://localhost:8095 (tools/article-editor/), mit Supabase-URL
und Publishable Key aus .env. Anmelden mit einem Dialy-Konto. Tab
Artikel: Beiträge anlegen, bearbeiten, löschen und wiederherstellen, mit
Vorschau wie in der App. Tab Lebensmittel: siehe unten. Tab Nachrichten:
siehe Kontaktformular unten.
Nur freigeschaltete Konten haben Zugriff:
- Einmalig
supabase/migrations/0006_article_editors.sqlim SQL Editor ausführen. - Konto freischalten:
insert into public.article_editors (user_id) select id from auth.users where email = '<deine E-Mail>';
Im Dialy-Editor, Tab Lebensmittel: alle Lebensmittel der App suchen (kuratiert, Marken, BLV, BLS, Open Food Facts), Name, Untergruppe, Nährwerte pro 100 g, GI, Kategorien und Portionen ändern, neue Lebensmittel anlegen und Lebensmittel deaktivieren (verschwinden aus dem KH-Rechner, lassen sich unter Inaktiv wieder aktivieren). Aus einer Kontakt-Nachricht führt Lebensmittel öffnen bzw. Als Lebensmittel anlegen direkt dorthin.
- Die Lebensmittel bleiben in der App gebündelt; Supabase (
foods) hält nur die geänderten, neuen und deaktivierten – je eine vollständige Zeile mit der Lebensmittel-ID der App. Die App holt neue Änderungen beim Start und beim Zurückkehren in den Vordergrund (ohne Konto), speichert sie lokal (food_edits, auch offline gültig) und legt sie über die gebündelten Werte – auch nach einem neuen BLV-/BLS-Import. - Einmalig
supabase/migrations/0010_food_edits.sqlim SQL Editor ausführen; Zugriff haben dieselben freigeschalteten Konten wie für die Artikel. Braucht Node 22.18 oder neuer (der Editor liest die.ts-Daten).
Nachrichten aus Kontakt (neues Lebensmittel / Fehler melden) landen in der
Tabelle contact_messages – ohne Mail-App. Einmalig
supabase/migrations/0007_contact_messages.sql im SQL Editor ausführen.
Senden darf jeder (auch ohne Konto); die App selbst kann nichts zurücklesen.
Lesen im Dialy-Editor, Tab Nachrichten (npm run editor, siehe oben):
offene / erledigte Nachrichten, Absender-E-Mail mit «Per E-Mail antworten»,
«Als erledigt markieren». Dafür einmalig zusätzlich
supabase/migrations/0008_contact_messages_editors.sql ausführen; Zugriff
haben dieselben freigeschalteten Konten wie für die Artikel.
„Passwort vergessen?“ auf der Anmeldeseite schickt einen Link per E-Mail; der Link öffnet die App, meldet über ihn an und fragt nach dem neuen Passwort. Damit Supabase in die App zurückleitet, die Adressen unter Authentication → URL Configuration → Redirect URLs erlauben:
http://localhost:8081(Web, Entwicklung)exp://**(Expo Go auf dem Handy)dialy://**(eigene iOS-/Android-Builds)
Lokale Konten (ohne Server) haben keine bestätigte E-Mail-Adresse und können deshalb nicht zurückgesetzt werden.
Es gibt kein echtes Bezahl-/IAP-SDK in diesem Build. Premium ist ein umschaltbares Flag:
- Konto-Tab → Premium-Karte lange drücken (≈0,5 s) schaltet Premium um, oder
- über die Mock-Paywall „Premium freischalten (Demo)“.
So lässt sich die komplette Premium-UI (volles Tagebuch / BZ-Auswertung) ohne echten Kauf testen.
Diese sind strukturiert vorhanden, aber hinter Flags deaktiviert, damit die App
ohne externe Konfiguration läuft. Im Code als TODO(native) markiert.
- Health-Import (CGM):
src/services/health.ts— Abstraktion mit Manuell-Fallback (immer verfügbar). Der native HealthKit / Health-Connect-Bridge braucht einen Dev/EAS-Build (nicht Expo Go) undEXPO_PUBLIC_FEATURE_HEALTH=true. - FCM (Push/Sync-Trigger):
src/services/fcm.ts+ Edge-Functionsupabase/functions/notify-sync. Ohne Firebase ist FCM ein No-op; der Client synchronisiert automatisch (siehe unten). - Sentry:
src/services/sentry.ts— No-op ohne DSN.
- Die lokale SQLite-DB ist die Quelle der Wahrheit für die UI.
- Schreibvorgänge landen zuerst lokal und werden in
sync_queuevorgemerkt. - Konfliktauflösung: Last-Write-Wins über
updated_at. - Löschungen sind weich (
deleted_at), nie physisch — so kann eine gelöschte Zeile auf einem anderen Gerät nicht wieder auftauchen. - Angemeldet bleiben ohne Internet: Die Sitzung wird beim Start vom Gerät gelesen (kein Server nötig), abgelaufene Tokens werden im Hintergrund erneuert, sobald wieder eine Verbindung besteht. Tagebuch & Speichern funktionieren offline; nur Anmelden/Registrieren braucht Internet.
- Sync-Trigger: automatisch – nach jeder lokalen Änderung (2 s gebündelt), beim App-Start, nach dem Anmelden und beim Zurückkehren in die App; im Browser zusätzlich, sobald die Verbindung zurück ist. Offline wiederholt die App den Versuch mit wachsenden Pausen (30 s bis 5 Min.). Einen Button gibt es nicht; unter Konto → „Synchronisierung“ steht nur der Status. FCM (Server stösst den Client an) ist vorbereitet, aber aus.
- Dark-Mode als Standard. Im Login-Tab unter „Darstellung“ lässt sich auf
Hell oder System (folgt der Geräte-Einstellung) umschalten. Die Wahl
wird gespeichert. Farben kommen ausschliesslich aus
src/theme/theme.ts(darkColors/lightColors) und werden überuseTheme()/useThemedStyles()gelesen. - Kein Medizinprodukt. Keine Insulindosis-Berechnung, keine Therapieempfehlung — eine bewusste rechtliche Grenze (hält Dialy ausserhalb EU‑MDR / Schweizer Medizinprodukte-Regulierung). Das „Insulinrate reduzieren (Pumpe)“-Feld im Sport-Screen ist eine reine Notiz zur eigenen Pumpeneinstellung, keine Empfehlung.
- Nicht veröffentlicht. Dieser Build ist nur für lokale Tests. Keine
Store-Submission, kein Release-Signing, kein
eas submit. - Swiss German. Alle Texte nutzen „ss“ statt „ß“.
Dialy/
├─ App.tsx # Root: Navigation + Bootstrap, Theme
├─ app.config.ts # Expo-Konfig (Darstellung, extra/env, Flags)
├─ src/
│ ├─ theme/theme.ts # Design-Tokens (Dark + Light, authoritativ)
│ ├─ state/ # Zustand-Store + Bootstrap-Hook
│ ├─ policy/gating.ts # Konto/Premium-Gating (Single Source of Truth)
│ ├─ db/ # expo-sqlite, Repositories, Seed
│ ├─ sync/ # Supabase-Client + Sync-Engine (§7-Vertrag)
│ ├─ services/ # auth, health, fcm, sentry (geflaggt)
│ ├─ components/ # UI-Bausteine (Screen, Button, Chart, …)
│ ├─ navigation/ # Tabs + Stacks
│ ├─ screens/ # Ratgeber, KH-Rechner, Tagebuch, Sport, Login
│ ├─ data/ # Lebensmittel (BLV, menuCH, kuratiert)
│ └─ utils/ # Format, IDs, Sport-Kurven-Heuristik
└─ supabase/
├─ migrations/ # Schema + RLS (SQL)
└─ functions/notify-sync/ # Edge-Function-Stub (Sync-Trigger)
npm start # = npx expo start
npm run android # start + open Android
npm run ios # start + open iOS (macOS only)
npm run web # start + open in the browser
npm run typecheck # TypeScript type check (tsc --noEmit)
npm run editor # Dialy-Editor (articles, foods, contact messages) on http://localhost:8095
# Local backend (Supabase in Docker) — see section 3C
npm run backend:start # boot local Supabase + auto-write .env
npm run backend:stop # stop containers (keeps data)
npm run backend:reset # re-apply migrations (wipes local data)
npm run backend:status # print local URLs + keys
npm run phone:link # forward Metro + Supabase ports to a USB Android deviceReleased under the MIT License © 2026 Olivier Lüthy. You're free to use, modify and distribute this software, including commercially, as long as the copyright notice and license are included.
Built by Olivier Lüthy — GitHub.