docs(rust): phase 11 as built and walked — §26.4-26.6, M16
The raid alert reached an emulator through ntfy.sh 19 seconds after a rig raid, titled from its inbox row; switched off in the app, the next raid enqueued in-app only. Closes M7's open QA item (on-device delivery against a live ntfy), and amends the M7 and engagement phase 8 passages D69 and D70 made stale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -229,7 +229,8 @@ Verified green: `:app:testDebugUnitTest` (18 new JVM tests — notifications DTO
|
||||
parse incl. malformed, topic/URL building, stream→route map + personal gating) + `:app:lintDebug` +
|
||||
`:app:assembleDebug`; backend 250 tests (+3 for `push.ntfyUrl`) and `npm run swagger` clean. The
|
||||
foreground-service tradeoff (§11) and the POST_NOTIFICATIONS runtime permission are implemented as
|
||||
planned; an on-device delivery pass against a live ntfy is the one open QA item.
|
||||
planned; an on-device delivery pass against a live ntfy is the one open QA item. *(Closed by M16 on
|
||||
2026-09-23: a raid alert delivered through `ntfy.sh` to an emulator, 19 seconds end to end.)*
|
||||
|
||||
**Part 2 (original plan) — the Android app.** UnifiedPush receiver + device registration
|
||||
against the merged Part-1 contract, a Notifications settings screen, and notification-tap deep-links.
|
||||
@@ -313,7 +314,10 @@ Work items:
|
||||
`GET …/streams` with a per-stream toggle bound to `GET/PUT …/subscriptions`; a **personal** stream
|
||||
(`requiresLinkedAccount`) is greyed with a "link a game account" hint until the user has a linked
|
||||
account — reuse the linked-accounts signal already fetched for M4's player surface
|
||||
(`PlayerShardRepository`), not a fresh source of truth. Toggling to a non-empty set triggers the
|
||||
(`PlayerShardRepository`), not a fresh source of truth. *(M16 amends both halves: the signal is now
|
||||
asked of the site's own module by capability (`LinkedAccountRepository`), because on a Rust site
|
||||
`PlayerShardRepository` answered no for everyone. It holds back only switching push **on**, and no
|
||||
longer greys the whole row.)* Toggling to a non-empty set triggers the
|
||||
register flow (#5) and requests `POST_NOTIFICATIONS`; emptying the set unregisters. Each mutation
|
||||
folds its `ApiResult` into a section-scoped, localized banner (§7 parity with M4).
|
||||
7. **Deep-links (resolves the §13 open item).** Tapping a notification opens the app to the stream's
|
||||
@@ -324,7 +328,8 @@ Work items:
|
||||
`label`) and deep-links — it does **not** pull `ref` content first; the content-free design means
|
||||
nothing needs decrypting to render the tap, and the target screen fetches fresh over the
|
||||
authenticated API on open. (Pulling `ref` for a richer inline notification is a possible later
|
||||
enhancement, not v1.)
|
||||
enhancement, not v1.) *(M16 did it for `notification:<id>` refs: `PushContentResolver` pulls the
|
||||
inbox row and titles the notification with it. Every other ref keeps the generic title.)*
|
||||
8. **Menu.** Add a **Notifications** entry to the signed-in group in `ui/navigation/Menu.kt` (near My
|
||||
Account), visible once signed in.
|
||||
9. **Permission UX.** Request `POST_NOTIFICATIONS` at the moment the user first enables a stream (API
|
||||
@@ -1528,7 +1533,7 @@ push, and Play (M6–M8) follow the designed app.
|
||||
once the fleet was widened to signed-in — the app's bearer session reaching the module's viewer
|
||||
check. Branch `fix/rust-presence-visibility`.
|
||||
|
||||
17. **M16 — a Rust notification on a phone** (post-v1; planned 2026-09-23). `module-rust` phase
|
||||
17. **M16 — a Rust notification on a phone** (post-v1; built 2026-09-23). `module-rust` phase
|
||||
11, R10's leg C: the app half of phase 10's notifications. **Design of record:
|
||||
[`../modules/rust/PLAN.md`](../modules/rust/PLAN.md) §26**, with its four decisions (D69–D72).
|
||||
|
||||
@@ -1542,14 +1547,38 @@ push, and Play (M6–M8) follow the designed app.
|
||||
channel **off** is never gated, and only switching push **on** needs a link.
|
||||
- **A push is titled from the item it points at.** A `notification:<id>` ref is pulled over the
|
||||
authenticated inbox API, and the relay still carries nothing. The per-stream title stays as the
|
||||
fallback and as the lock-screen version. §11's "nothing is fetched to show a notification"
|
||||
stops being true, and it gets rewritten when this lands.
|
||||
fallback and as the lock-screen version. The M7 items that said nothing is
|
||||
fetched to show a notification are amended in place.
|
||||
- **`/player/rust` and `/rust/servers/<id>?tab=<tab>` open natively.** M15 deferred the first
|
||||
of these to exactly this phase.
|
||||
|
||||
It also closes **M7's open QA item**. On-device delivery against a live ntfy has never been
|
||||
walked. It is walked here, on `ntfy.sh` (D72).
|
||||
|
||||
#### As built (2026-09-23)
|
||||
|
||||
Branch `feat/rust-phase-11-notifications`. The app suite is **672 tests, 0 failures**, up from
|
||||
657, with `lintDebug` and `assembleDebug` green. It was walked on the `s22_ultra` emulator
|
||||
against real core on `main`, with the module installed, the protocol-7 sidecar inside the Oxide
|
||||
rig's container, and the relay on `ntfy.sh`:
|
||||
|
||||
- **Linked (raidowner1):** the raid alert's push switched on, and the device registered
|
||||
`https://ntfy.sh/<topic>`. A door raided with phase 10's rig helper reached the phone
|
||||
**19 seconds** later as *"Your base is being raided — A door was destroyed in P16 on Oxide
|
||||
rig."* The notification was `vis=PRIVATE`, with a public version carrying only the generic
|
||||
title. Tapping it opened the inbox, and the row opened the Oxide rig's screen natively.
|
||||
- **Switched off in the app:** push off, and the next raid enqueued in-app only. The inbox row
|
||||
arrived and no system notification was posted. With nothing else on push, the device was
|
||||
unregistered and the push service stopped.
|
||||
- **Not linked (walkadmin):** push held back with the hint, while email and in-app stayed live.
|
||||
In-app was switched off, stored as `off`, and switched back on.
|
||||
- **D71:** a row linking `?tab=leaderboard` opened on the Leaderboard tab, and `/player/rust`
|
||||
opened *My Rust account*. `?tab=clans` went to a Custom Tab.
|
||||
|
||||
**Not walked:** a UO site (no UO core was running; the path is the call it always was, and a test
|
||||
pins it), and the lock-screen rendering (the emulator has no screen lock; the public version was
|
||||
read back from `dumpsys notification`).
|
||||
|
||||
### Deferred (not a milestone)
|
||||
|
||||
- **Platform Teams in the app** — **deferred 2026-08-17, no app work scheduled.** The website is
|
||||
@@ -1772,7 +1801,9 @@ of push.
|
||||
push streams, so `team.forum.post` would have landed on Home. `Routes.forTickle(stream, ref)` sends
|
||||
anything whose ref starts with `notification:` to the inbox and leaves every other tickle on the
|
||||
route it has always had. The ref is never decoded past that prefix and never rendered - it is a hint
|
||||
that a row exists, and the contract stays wake-and-pull.
|
||||
that a row exists, and the contract stays wake-and-pull. *(M16 decodes it once more, to the id,
|
||||
and only to pull that row over the authenticated inbox API and title the notification with it. It is
|
||||
still never rendered.)*
|
||||
|
||||
**The settings screen** (`NotificationSettingsScreen` + `NotificationSettingsViewModel`) moved off
|
||||
`/notifications/subscriptions` onto `/notifications/channels`. Controls are rendered from the wire:
|
||||
|
||||
Reference in New Issue
Block a user