feat(safety): report, block and hide chat lines in the app #64

Merged
whitlocktech merged 1 commits from feat/user-safety into edge 2026-10-30 10:30:17 -05:00
Member

What & why

Step 4 of docs/website/USER_SAFETY.md (§4.3): the app half of report, block and hide. Core shipped its half in website#233 and the Rust module its half in Module-Rust#72. This makes the Play content-rating answer ("users or user-generated content can be reported") true in the app, where the Rust chat feed is the user-generated content.

A Rust chat line's long-press menu (ui/rust/ChatSafetySheet.kt, ChatSafetyViewModel.kt, rules in ChatSafety.kt):

  • Signed out: Sign in to report or block, which opens the login screen (S6).
  • Members: Report (the six reasons and an optional detail), Hide for me, and Block player where the line says canBlock. Block asks for confirmation and says the player isn't told.
  • Admins and moderators also get Hide for everyone, or Show to everyone again on a hidden line.
  • Only chat lines answer the press; deaths and connects don't.
  • 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 re-read, never edited. The server filters it per reader (S11), so after a hide, a block or a staff hide the app asks again (RustServerViewModel.reloadFeed). A report changes nothing and does not re-read. Staff see a hidden line dimmed and marked Hidden by staff (S10).

Account → Blocked (ui/auth/BlockedScreen.kt, from My account), mirroring the web panel:

  • Account blocks by username; Steam-ID blocks by ID and "not linked to an account".
  • A block whose target is now staff says Not applied and why (S8).
  • Unblock per row, and Show them again for hidden items.

Data: core's routes on a new SafetyApi + SafetyRepository; the module's two staff routes on RustApi. RustEventDto gains canBlock and hidden. The block DTO is UserBlockDto, because BlockDto is the CMS page block.

Strings: the chat menu's strings are in strings_rust.xml (only Rust screens use them), and Blocked's are in core's strings.xml. StringVocabularyTest allows no fourth strings file.

Choices made without asking (please check)

  • Staff hide-for-everyone is in the app menu too. §4.3 says "the same menu", and the web menu has it.
  • Back on the report form or the block confirmation returns to the menu instead of closing the sheet, and the sheet opens fully expanded. Both came from the walk: Back to hide the keyboard was closing the sheet and losing a half-written report, and Send report sat below the fold.

How it was tested

  • ./gradlew testDebugUnitTest lintDebug assembleDebug (JDK 21, -Pksp.incremental=false locally): 827 tests, 0 failing. 29 are new: ChatSafetyTest, ChatSafetyViewModelTest, SafetyRepositoryTest, BlockedViewModelTest. No lint findings in the new code.
  • Emulator walk on Medium_Phone_API_36.1. It ran against a core at the website#233 merge, on its own port, with Module-Rust#72 installed and a throwaway database (chat lines and Steam links written straight into the tables):
    • Signed out: the long press offered only Sign in, which opened login.
    • As a member:
      • A report reached content_reports with its reason, detail and snapshot. A second report was refused as already reported.
      • Hide for me took one line out.
      • Blocking an unlinked speaker made a Steam-ID block. It took their chat out of the feed, but not their death.
      • Blocking a linked speaker made an account block.
      • The moderator's line and the member's own line offered no Block. A death offered no menu.
    • Account → Blocked:
      • It listed both blocks.
      • 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, removed it from a signed-out read, and wrote rust.chat.hide to the activity log. Show to everyone again undid it.

Docs: RunicGateway/docs docs/user-safety-android.

Checklist

  • I have read CONTRIBUTING.md.
  • The change builds and existing tests/checks pass locally.
  • I have added or updated tests/docs where it makes sense.
  • My commits are reasonably scoped with clear messages.

AI-assisted contributions (required)

  • No AI tools were used to produce this contribution.
  • AI tools were used. Tool(s): Claude Code (Claude Opus 5.5). I have reviewed and understand
    every change, and take responsibility for it. AI-authored commits are
    marked with a Co-Authored-By / Assisted-By trailer.

License

  • I agree that my contribution is licensed under this project's license
    (GNU GPL v3.0 or later), and I have the right to contribute it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY

## What & why Step 4 of `docs/website/USER_SAFETY.md` (§4.3): the app half of report, block and hide. Core shipped its half in website#233 and the Rust module its half in Module-Rust#72. This makes the Play content-rating answer ("users or user-generated content can be reported") true in the app, where the Rust chat feed is the user-generated content. **A Rust chat line's long-press menu** (`ui/rust/ChatSafetySheet.kt`, `ChatSafetyViewModel.kt`, rules in `ChatSafety.kt`): - Signed out: *Sign in to report or block*, which opens the login screen (S6). - Members: *Report* (the six reasons and an optional detail), *Hide for me*, and *Block player* where the line says `canBlock`. Block asks for confirmation and says the player isn't told. - Admins and moderators also get *Hide for everyone*, or *Show to everyone again* on a hidden line. - Only chat lines answer the press; deaths and connects don't. - 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 re-read, never edited.** The server filters it per reader (S11), so after a hide, a block or a staff hide the app asks again (`RustServerViewModel.reloadFeed`). A report changes nothing and does not re-read. Staff see a hidden line dimmed and marked *Hidden by staff* (S10). **Account → Blocked** (`ui/auth/BlockedScreen.kt`, from *My account*), mirroring the web panel: - Account blocks by username; Steam-ID blocks by ID and "not linked to an account". - A block whose target is now staff says *Not applied* and why (S8). - *Unblock* per row, and *Show them again* for hidden items. **Data:** core's routes on a new `SafetyApi` + `SafetyRepository`; the module's two staff routes on `RustApi`. `RustEventDto` gains `canBlock` and `hidden`. The block DTO is `UserBlockDto`, because `BlockDto` is the CMS page block. **Strings:** the chat menu's strings are in `strings_rust.xml` (only Rust screens use them), and Blocked's are in core's `strings.xml`. `StringVocabularyTest` allows no fourth strings file. ### Choices made without asking (please check) - **Staff hide-for-everyone is in the app menu too.** §4.3 says "the same menu", and the web menu has it. - **Back on the report form or the block confirmation returns to the menu** instead of closing the sheet, and the sheet opens fully expanded. Both came from the walk: Back to hide the keyboard was closing the sheet and losing a half-written report, and *Send report* sat below the fold. ## How it was tested - `./gradlew testDebugUnitTest lintDebug assembleDebug` (JDK 21, `-Pksp.incremental=false` locally): **827 tests, 0 failing**. 29 are new: `ChatSafetyTest`, `ChatSafetyViewModelTest`, `SafetyRepositoryTest`, `BlockedViewModelTest`. No lint findings in the new code. - **Emulator walk** on `Medium_Phone_API_36.1`. It ran against a core at the website#233 merge, on its own port, with Module-Rust#72 installed and a throwaway database (chat lines and Steam links written straight into the tables): - Signed out: the long press offered only *Sign in*, which opened login. - As a member: - A report reached `content_reports` with its reason, detail and snapshot. A second report was refused as already reported. - *Hide for me* took one line out. - Blocking an unlinked speaker made a Steam-ID block. It took their chat out of the feed, but not their death. - Blocking a linked speaker made an account block. - The moderator's line and the member's own line offered no *Block*. A death offered no menu. - *Account → Blocked*: - It listed both blocks. - 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, removed it from a signed-out read, and wrote `rust.chat.hide` to the activity log. *Show to everyone again* undid it. Docs: RunicGateway/docs `docs/user-safety-android`. ## Checklist - [x] I have read [CONTRIBUTING.md](CONTRIBUTING.md). - [x] The change builds and existing tests/checks pass locally. - [x] I have added or updated tests/docs where it makes sense. - [x] My commits are reasonably scoped with clear messages. ## AI-assisted contributions (required) - [ ] No AI tools were used to produce this contribution. - [x] AI tools were used. Tool(s): `Claude Code (Claude Opus 5.5)`. I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a `Co-Authored-By` / `Assisted-By` trailer. ## License - [x] I agree that my contribution is licensed under this project's license (**GNU GPL v3.0 or later**), and I have the right to contribute it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude added 1 commit 2026-10-30 09:57:35 -05:00
feat(safety): report, block and hide chat lines in the app
All checks were successful
PR Checks / android-build (pull_request) Successful in 24m48s
5386889ef4
USER_SAFETY.md step 4 (§4.3): the app half of report, block and hide.

- Long-press a Rust chat line for its menu: Report (the six reasons and an
  optional detail), Hide for me, Block player where the line says canBlock,
  and for admins and moderators Hide for everyone, or Show to everyone again
  on a hidden line. Signed out, it says Sign in to report or block and opens
  the login screen (S6).
- The feed is re-read after a hide, a block or a staff hide, never edited in
  the app: the server filters it per reader (S11). A report changes nothing
  and does not re-read.
- Staff see a line hidden for everyone dimmed and marked Hidden by staff (S10).
- Account -> Blocked lists account and Steam-ID blocks with Unblock, says when
  a block no longer applies because its target is now staff (S8), and offers
  Show them again for hidden items.
- Core's routes on a new SafetyApi; the module's two staff routes on RustApi.
  RustEventDto gains canBlock and hidden.

Two fixes from the emulator walk: Back on the report form or the block
confirmation now steps back to the menu instead of closing the sheet (Back
to put the keyboard away was throwing a half-written report away), and the
sheet opens fully so Send report is on screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
whitlocktech merged commit b20abdf4ba into edge 2026-10-30 10:30:17 -05:00
whitlocktech deleted branch feat/user-safety 2026-10-30 10:30:18 -05:00
Sign in to join this conversation.
No description provided.