chore(release): cut edge over to main — Google Play, account deletion, report/block/hide (v0.7.0) #65

Merged
whitlocktech merged 11 commits from edge into main 2026-10-30 17:26:19 -05:00

11 Commits

Author SHA1 Message Date
6630d05e76 Merge branch 'main' into edge
All checks were successful
PR Checks / android-build (pull_request) Successful in 3m3s
2026-10-30 22:23:03 +00:00
b20abdf4ba Merge pull request 'feat(safety): report, block and hide chat lines in the app' (#64) from feat/user-safety into edge
All checks were successful
PR Checks / android-build (pull_request) Successful in 4m41s
Reviewed-on: #64
2026-10-30 15:30:14 +00:00
5386889ef4 feat(safety): report, block and hide chat lines in the app
All checks were successful
PR Checks / android-build (pull_request) Successful in 24m48s
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
2026-10-30 09:56:25 -05:00
d0c5bd6ddf Merge pull request 'feat(account): delete my account in the app' (#63) from feat/account-deletion into edge
Reviewed-on: #63
2026-10-30 05:27:57 +00:00
6be83fe70a feat(account): delete my account in the app
All checks were successful
PR Checks / android-build (pull_request) Successful in 14m55s
ACCOUNT_DELETION.md §4.3 (step 4). My account gains a Delete my account
row, last and set apart, which opens its own screen: what goes, what stays,
the staff note, then the typed username, the password and, when two-factor is
on, an authenticator or recovery code. Delete stays off until the name matches.

MeApi.deleteAccount sends DELETE /auth/me/account and reads the raw response,
so each refusal's code becomes its own sentence (the website's words). A
wrong password is a 400 (AD12), so the authenticator never replays it.

On success AuthRepository.forgetDeletedAccount does what a sign-out does
without its server calls: the inbox snapshot, the trust token (which a
sign-out keeps, but the account is gone) and the session, whose teardown
also drops the push registration. It is not cancellable. The route holds the
view model, so it tells a deletion from any other sign-out, and opens sign-in
with "Your account has been deleted." on the shell's snackbar.

Tests: the refusal mapping per code, 429, unknown codes, no answer, the
request body; the confirm rule, the staff rule, each refusal's sentence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-30 00:13:34 -05:00
19c2f77a98 Merge pull request 'fix(push): hold the ntfy connection as specialUse and restart it on boot' (#62) from fix/push-special-use into edge
Reviewed-on: #62
2026-10-29 13:10:17 +00:00
690441a8c3 fix(push): hold the ntfy connection as specialUse and restart it on boot
All checks were successful
PR Checks / android-build (pull_request) Successful in 24m52s
PushService was a dataSync foreground service. Android 15 stops dataSync
after six hours a day and expects the service to stop itself on
onTimeout; it never did, so with target 36 the app would crash. A held-open
push connection is not a transfer either, which is what Play reserves
dataSync for.

- The service is now specialUse, with the PROPERTY_SPECIAL_USE_FGS_SUBTYPE
  reason Play reviews (ntfy does the same on Play). No daily limit.
- PushBootReceiver restarts it after BOOT_COMPLETED and MY_PACKAGE_REPLACED
  when push was on and a registration is saved (a sign-out clears it). It
  starts the service from the saved topic instead of re-registering, since
  the network is often not up at boot.

PLAN.md M8, decided by the org lead 2026-10-29.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-29 07:47:43 -05:00
ac60427a3e Merge pull request 'feat(demo): explain the public demo's refusals and show its strip on Home' (#61) from feat/demo-mode into edge
Reviewed-on: #61
2026-10-26 14:18:12 +00:00
1579a1982c Merge pull request 'feat(release): target API 36 and upload each tag's bundle to Google Play (M8 step 1)' (#60) from feat/m8-google-play into edge
Reviewed-on: #60
2026-10-26 14:17:56 +00:00
aca358d72b feat(demo): explain the public demo's refusals and show its strip on Home
All checks were successful
PR Checks / android-build (pull_request) Successful in 20m2s
Step 4 of docs website/DEMO_MODE.md §10 (website#222).

- DemoLockInterceptor on the main client peeks every 403 body. On
  code DEMO_LOCKED it reports the server's sentence to DemoNotices, and the
  shell shows it as one snackbar, like the web's toast (DM19). A newer refusal
  replaces the one showing. Screens added later get it without doing anything.
- ApiResult.HttpError carries the body's code (read only on a 403, peeked so
  the body is not consumed); isDemoLocked(). ErrorKind.DEMO_LOCKED is mapped
  ahead of the status in toUiState and toShardUiState, and is not retryable.
- Screens whose 403 copy blames the role now say the demo sentence instead:
  the four admin view models, Characters' account creation, and the contact
  form's line (the contact form is locked on the demo).
- Home draws the strip from GET /public/settings' demo block: the wipe time in
  UTC, the published usernames, and the site's sign-in page for the passwords.
  The app does not list the passwords (DM20). A real shard sends no block, so
  it draws nothing.

Checked on an emulator against website main (69998ec) with DEMO_MODE=true and
seed:demo: the strip on Home; demo-player's username change refused with the
snackbar under the screen's own line; demo-admin's site-mode switch refused
with the demo sentence in place of "You don't have permission".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-26 06:29:16 -05:00
7a8231ba9c feat(release): target API 36 and upload each tag's bundle to Google Play
All checks were successful
PR Checks / android-build (pull_request) Successful in 21m22s
Step 1 of M8 (docs/android/PLAN.md item 9).

- compileSdk/targetSdk 36 (Play's floor since 2026-08-31). AGP 8.6.1
  cannot build against 36, so AGP goes to 8.13.2 (the last 8.x) with the
  Gradle 8.14.3 wrapper. Kotlin, KSP and Hilt are unchanged. All three
  workflows install platforms;android-36; AGP 8.13 still defaults to
  build-tools 35.0.0.
- release.yml also runs bundleRelease, signed with the same keystore,
  and attaches the .aab to the Gitea release beside the APK (the first
  bundle has to be uploaded by hand, and this is where it comes from).
- A last step uploads the bundle to Play's internal track through the
  Play Developer API v3 edit flow. The OAuth token comes from a
  service-account JWT signed with openssl; the key is the new
  PLAY_SERVICE_ACCOUNT_JSON secret. While that secret is unset, the
  step warns and skips. The step runs after the Gitea release, so a
  Play failure never holds back the APK.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-23 07:03:54 -05:00