docs(mobile): document the full push-notification setup and its gotchas

Push notifications went from "wired but not deliverable" to fully working
end-to-end this session. Records the three separate one-time setup pieces
(EAS project id, FCM credentials baked at prebuild time, and the FCM V1
vs. Legacy service-account-key slot mixup that produced a confusing
"Unable to retrieve the FCM server key" error) so a future session doesn't
have to rediscover them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Sonnet 5 committed 2026-09-06 14:28:03 +02:00
1 parent fcb5ae3eef
commit a890904b6c
1 file changed
+54 -16
+54 -16
View File
@@ -17,10 +17,10 @@ comments, repost, pet profile detail (view/follow/edit/followers-following),
stories (tray/creation/viewer), video playback, search, health tracking
(consent gate + weight/vet/vaccine logs), milestones, and message
sending/receiving are done and verified end-to-end against the live backend.
Push notifications are wired end-to-end but not yet deliverable — see "Push
notifications" below. Android is the priority platform; iOS builds need a
Mac + Apple Developer account and come later (same codebase, no separate
source tree — see "Android-first, then iOS" below).
Push notifications are fully working — see "Push notifications" below.
Android is the priority platform; iOS builds need a Mac + Apple Developer
account and come later (same codebase, no separate source tree — see
"Android-first, then iOS" below).
## Architecture
@@ -206,9 +206,10 @@ separately. "Starting with Android" means:
budgeted in `../mobil.md`) before an internal test track can be created.
- iOS needs a Mac (for `expo run:ios` / EAS builds with credentials) and an
Apple Developer account (99 €/year) — deferred until Android is validated.
- EAS Build (`eas.json`, not yet added) is the recommended path for
producing signed Android/iOS binaries without a local Android
Studio/Xcode toolchain; add it when a first real build is needed.
- EAS Build (`eas.json` — added, currently just default profiles) is the
recommended path for producing signed Android/iOS binaries without a
local Android Studio/Xcode toolchain; no EAS Build has been run yet, the
file only exists so far because `eas credentials` requires it.
## What's not built yet
@@ -229,17 +230,54 @@ emulator. Still missing, roughly in the order they'd unblock the most:
- No i18n — the whole app is hardcoded German text, matching the sign-in screen
### Push notifications — wired but not yet deliverable
### Push notifications
`usePushNotifications` (in `app/_layout.tsx`) requests notification
permission and registers the resulting Expo push token with the backend
(`owner.registerPushToken`); `src/lib/notify.ts` on the web side sends a
best-effort push through Expo's push API alongside every in-app
Notification it creates (follow, reaction, comment, message, ...). Minting
a real token needs an **EAS project id** (`app.json`'s
`extra.eas.projectId`, set once via `eas init` — free, no EAS Build
required, but does need an Expo account login). Until that's done, the
permission prompt shows and is granted, but `getExpoPushTokenAsync` has
nothing to attach to and the hook no-ops with a console warning
(`[push] No EAS project id configured...`) — confirmed via `adb logcat` on
2026-09-06. No Owner row has an `expoPushToken` set until this is run.
Notification it creates (follow, reaction, comment, message, ...).
Verified end-to-end on 2026-09-06: permission → real Expo push token →
registered in the DB → delivered to the Android emulator's notification
tray.
Getting here needed three separate one-time setup pieces, each easy to
half-do and end up with a silent non-working state:
1. **EAS project id** (`app.json`'s `extra.eas.projectId`) — via
`eas login` + `eas init --account pawfeed`. Without this,
`getExpoPushTokenAsync` has nothing to attach to and the hook no-ops
with `[push] No EAS project id configured...`.
2. **FCM credentials for Android** (`app.json`'s `googleServicesFile` →
`google-services.json`, gitignored — redownload from the Firebase
Console's `org.pawfeed.app` Android app on a fresh clone). This is
baked into the native project at `expo prebuild` time, so adding it
needs a real rebuild (`expo prebuild --platform android` + `expo run:android`),
unlike the EAS project id which the dev client picks up from a plain
JS reload.
3. **The FCM *service account key* uploaded to EAS** via
`eas credentials -p android` (needs `eas.json` to exist first, hence
that file). The credentials manager has two separate Android push
slots — **"FCM Legacy"** (a plain API key string, and Google shut the
underlying Legacy API down in 2024) and **"FCM V1"** (a Google Service
Account JSON key, what Firebase Console's "generate new private key"
actually gives you). Uploading the service-account JSON to the wrong
slot leaves EAS's send API returning
`Unable to retrieve the FCM server key for the recipient's app` even
though a real push token exists and permission is granted — that
symptom means step 3 landed in the wrong slot, not that steps 1–2 are
broken.
Also note: `getExpoPushTokenAsync` can hang indefinitely rather than
reject if the device can't reach Google's registration backend — the hook
wraps it in a 15s timeout so a real failure surfaces instead of permanent
silence.
**Deploy note:** `owner.registerPushToken` is a *web* backend change.
Since the mobile app's default `EXPO_PUBLIC_API_URL` is the live
`pawfeed.org` deployment, this endpoint doesn't exist there until the web
app is redeployed too (`ssh` to the NAS, `git pull`, `./docker/rolling-deploy.sh`
— see root `CLAUDE.md`'s deployment section). This is the one case where
a "mobile" feature session still needs a web deploy — [[project_mobile_deployment_target]]'s
"mobile changes need no NAS deploy" is about `mobile/` code itself, not
backend endpoints mobile happens to call.