Merge pull request #1194 from J3vb/feat/dm-block-gating-updater-progress

feat(client): DM block composer gating + updater download progress
This commit is contained in:
J3vb
2026-07-20 12:39:35 +02:00
committed by GitHub
16 changed files with 553 additions and 32 deletions
+16 -6
View File
@@ -144,12 +144,22 @@ and `IsEitherBlocked` is bidirectional). **Target UX:**
| Being blocked | Composer read-only with a neutral "You can't message this user right now." (do not reveal the block state explicitly — the server returns a generic refusal) |
| Unblock | Composer re-enables |
> **⚠ Current gap.** There is no client-side block-state composer gating. The
> composer now has a disabled-with-reason mode (see [messaging.md §2](messaging.md)),
> but DM channels are left ungated: the block/unblock REST surface exists
> server-side, and the client refuses a DM send only via the failed-row /
> `FORBIDDEN` path today. Target ties DM block state into the same
> composer-state machine.
> **✅ Wired (composer gating).** DM block state now drives the same
> disabled-with-reason composer mode (see [messaging.md §2](messaging.md)) via
> `blocks.store`. `blockedByMe` is loaded authoritatively from `GET /blocks` on
> every `ready` (`dispatcher.ts`) → the explicit "You've blocked this user…"
> reason. `blockedByThem` is inferred from a refused DM send (`ErrBlocked` →
> `FORBIDDEN`, bidirectional) → the neutral "You can't message this user right
> now." reason, and is cleared on the next `ready` so a reconnect re-evaluates.
> `ChannelController` reads `dmComposerBlockReason(recipientId)` and subscribes to
> `blocks.store`, so an unblock (shrunken `GET /blocks`) re-enables the composer
> live. `blockedByMe` takes precedence when both directions apply.
>
> **Remaining gap.** There is no in-client **block button** yet (the block/unblock
> REST surface exists server-side; blocks made from the web panel or a prior
> session are honoured via `GET /blocks`). Adding the block affordance to the DM
> profile sidebar would call `PUT/DELETE /blocks/{userId}` and update `blocks.store`
> directly for an instant local un-gate.
---
+8 -5
View File
@@ -171,14 +171,17 @@ sequenceDiagram
|-------|--------------|
| checking | Silent (no UI until a result) |
| available | Non-modal banner with version + Update Now / Later (already `UpdateNotifier.ts:30-62`) |
| downloading | Banner "Downloading update…" |
| downloading | Banner "Downloading update… N%" (or "… N.N MB" until Content-Length is known) |
| applied | App relaunches automatically |
| failed | "Update failed. Please try again later." + Dismiss |
> **⚠ Current gap — no download progress.** The download callback is a no-op
> (`update_commands.rs:177`), so "Downloading update…" has no percentage. For a
> large binary this looks hung. Target: surface a progress indicator (percentage
> or indeterminate-with-bytes) by wiring the plugin's progress callback.
> **✅ Wired — download progress.** The Rust download callback
> (`download_and_install_update` in `update_commands.rs`) accumulates received
> bytes and emits an `update-progress` event (`{ received, total }`) to the
> webview. `downloadAndInstallUpdate(serverUrl, onProgress)` (`updater.ts`) listens
> for it and forwards to `UpdateNotifier`, whose `formatDownloadProgress` renders a
> percentage when `total` is known and falls back to bytes (MB) otherwise, so the
> banner never looks hung. (Rust change is minimal and CI-gated only.)
---