docs(safety): record USER_SAFETY step 4, the app's chat-line menu

§4.3 rewritten to match what was built: the long-press sheet and what it
offers, the refusals, the re-read after a change, the Hidden by staff marker,
and Account -> Blocked. §7 step 4 recorded as built and walked on the
emulator, with the two things the walk found. android/PLAN.md M8 step 4
points at it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-30 09:57:06 -05:00
parent 76572fe3b4
commit c0bec41967
2 changed files with 38 additions and 8 deletions

View File

@@ -1062,7 +1062,8 @@ push, and Play (M6–M8) follow the designed app.
and it takes `MODULE_API` 1.20.0 (org lead, 2026-10-29). The app's part, the *Delete my
account* screen reached from *My account*, was built 2026-10-30 (that plan's §6 step 4).
4. **Report, block and hide** across `website`, `Module-Rust`, the kit and this app
(`USER_SAFETY.md` §7), now `MODULE_API` 1.21.0.
(`USER_SAFETY.md` §7), now `MODULE_API` 1.21.0. The app's part, a long-press menu on Rust
chat lines and *Account → Blocked*, was built 2026-10-30 (that plan's §7 step 4).
5. **The cutover**, `edge` → `main`, once 1-4 have landed, so the first Play bundle carries
all of it. **Before it merges, the org lead enrolls the existing keystore as the app-signing
key** in Play Console (App integrity, "use my own key"). `PLAY_SERVICE_ACCOUNT_JSON` is

View File

@@ -1,7 +1,7 @@
# Report, block and hide — user safety on the website and in the app
**Status:** plan of record, 2026-10-29. Steps 2 (core) and 3 (Module-Rust, Integration Kit) built
2026-10-30 (§7); the rest not yet.
**Status:** plan of record, 2026-10-29. Steps 2 (core), 3 (Module-Rust, Integration Kit) and 4
(Android) built 2026-10-30 (§7); the rest not yet.
Each phase stops for the org lead before the next one starts. Built after account deletion
([`ACCOUNT_DELETION.md`](ACCOUNT_DELETION.md)), which is why it is `MODULE_API` 1.21.0. §4 was
amended by S11-S14 (§5) when the core was built.
@@ -148,11 +148,22 @@ All of it is in `server/safety.js`, beside the events route that uses it.
### 4.3 Android app (chat lines only, S4)
- **Long-press a chat line** for the same menu. Report opens a sheet with the six reasons and an
optional detail. A member who is signed out gets *Sign in to report or block*.
- **Account → Blocked** lists blocks and undoes them, mirroring the web page.
- The feed is already filtered by the server (S11); the app refetches after a block or a hide and
offers *Block player* only where the line says `canBlock`.
- **Long-press a chat line** for the same menu, in a bottom sheet: *Report* (the six reasons and
an optional detail), *Hide for me*, *Block player* (where the line says `canBlock`, after a
confirmation that says the player is not told) and, for admins and moderators, *Hide for
everyone* or, on a hidden line, *Show to everyone again*. Only chat lines answer the press. A
member who is signed out gets *Sign in to report or block*, which opens the app's login screen.
Back on the report form or the block confirmation returns to the menu, keeping what was typed.
- The calls are core's (`/player/reports`, `/auth/me/blocks`, `/auth/me/hidden`) and the
module's two staff routes. Each refusal has its own sentence: already reported or blocked,
gone, staff (S8), too many reports, the module not answering, the demo's lock.
- The feed is already filtered by the server (S11); the app reads it again after a hide, a block
or a staff hide, and never drops a line itself. A report changes nothing, so it does not. Staff
see a line hidden for everyone dimmed and marked *Hidden by staff* (S10).
- **Account → Blocked** (from *My account*) lists blocks with *Unblock*, mirroring the web page:
an account block by its username, a Steam-ID block by its ID and "not linked to an account", and
a block whose target is now staff with *Not applied* and why (S8). *Show them again* clears the
member's hides.
## 5. Further decisions (org lead, 2026-10-29)
@@ -224,6 +235,24 @@ Play declarations gain "user-generated content: collected". M21 says so.
a moderator *Hide for everyone* / *Show to everyone again* with the *Hidden by staff* marker and
the Reports queue showing both chat-line reports from their snapshots. No console errors.
4. **Android** (`edge`): §4.3. One PR.
**Built 2026-10-30** as Android-app `feat/user-safety`. `./gradlew testDebugUnitTest lintDebug
assembleDebug` green: 827 tests, 29 of them new (the menu's rules, the repository's refusals,
both view models), and no lint findings in the new code. Walked on the `Medium_Phone_API_36.1`
emulator against a core on its own port with Module-Rust installed and a throwaway database,
chat lines and Steam links written into the tables: signed out, a long press offered only
*Sign in*, which opened the login screen; as a member, a report reached `content_reports`
with its reason, detail and snapshot, and a second one was refused as already reported; *Hide
for me* took one line out; blocking an unlinked speaker made a Steam-ID block and took their
chat (not their death) out; blocking a linked speaker made an account block; the moderator's
line and the member's own offered no *Block*, and a death offered no menu. *Account →
Blocked* listed both; linking the Steam ID turned that block into an account block (S14);
making that account a moderator showed *Not applied* (S8); *Unblock* and *Show them again*
brought the lines back. As a moderator, *Hide for everyone* dimmed and marked the line for
staff, took it out of a signed-out read and wrote `rust.chat.hide` to the activity log, and
*Show to everyone again* undid it. The walk found two things, both fixed: Back to put the
keyboard away closed the whole sheet and lost a half-written report, and the half-open sheet
left *Send report* below the fold.
5. **A walk** on the demo after the next wipe: report, hide and block, each on the web and in the
app; *Hide for everyone* from the queue; a blocked Steam ID that later links to an account stays
blocked.