Turn on push from onboarding step 3 and add a test-push endpoint (Feature 19)
CI / ci (push) Successful in 6m59s
CI / ci (pull_request) Successful in 7m2s

Onboarding step 3's "Turn on notifications" now calls usePush().subscribePush
straight from that tap (the only place onboarding asks for permission) and
shows how it ended: on; blocked, with how to allow it in the browser's site
settings; not supported, with how to add MealQuest to the Home Screen on
iPhone; or a dismissed prompt that can be asked again. "Maybe later" still
finishes without asking. The interim notificationsUnavailable is gone.

POST /api/v1/settings/notifications/test-push queues "MealQuest test
notification" through the notification outbox as a plan_ready event with a
fresh plan_ready:test-<ULID> key, opening /plan, on the push channel only
(new notify.Service.EmitPush), never Google Chat. It answers 202
{event_key, subscriptions}, or 409 no_subscriptions / push_off rather than
reporting success when nothing would be sent.

Maestro task f89279b8-b92c-4d9f-a6c2-fd87fccda509.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 16:54:23 -04:00
co-authored by Claude Opus 5.5
parent 51e0213e92
commit 875a0faaae
12 changed files with 505 additions and 139 deletions
+11
View File
@@ -706,6 +706,17 @@ Runs are recorded in `schedule_runs` and executed through `jobs`.
`{"endpoint"}` and answers 204 whether or not it was stored.
- An invalid body is 400 `invalid_request` (not a JSON object of the
known fields) or `invalid_subscription` (a field fails validation).
- `POST /api/v1/settings/notifications/test-push` (CSRF-checked) checks
delivery to the household's devices. It queues "MealQuest test
notification" through `notification_outbox`, so it takes the real
delivery path with its retries. It is a `plan_ready` event whose tap
opens `/plan`. Its key is `plan_ready:test-<ULID>`, new on every call,
so `Emit` never deduplicates it. It goes to the `push` channel only
(`notify.Service.EmitPush`), never to the family Google Chat space.
It answers 202 `{event_key, subscriptions}`. Two cases send nothing
and answer 409 rather than report success: no device has subscribed
(`no_subscriptions`), or push is switched off for `plan_ready` in
the preferences (`push_off`).
- `internal/chat` keeps the Google Chat incoming webhook in `chat_settings`
and is the outbox's sender for the `chat` channel. The webhook URL holds
its own key and token, so it is sealed with `secretbox` in