Files
amethyst/docs
Claude 4bb44de5e2 feat: explain every reason a chat room blocks posting
A boolean `canPost()` can hide the composer but cannot say why, so each new
gate degraded into blank space above the keyboard. That is how a banned
Concord member came to get an empty slot and no explanation: the screen's
only explanatory branch tested `dissolved`.

Model the reason instead. `PostingGate` is a sealed interface — Allowed, or
a Blocked subtype (Banned, Dissolved, NoKey, NotAMember, InviteOnly) — and
both channel types now derive it, with `canPost()` defined as
`postingGate() == Allowed` so the answer and the explanation cannot drift.
Adding a gate later is a compile error at the render site until it is given
copy of its own, which is the property the boolean could not express.

One `PostingGateNotice` renders all of them, replacing the Concord-only
dissolved notice and the NIP-29-only join notice; the forum thread list
reuses the same sentence instead of a flat "read-only". The Concord screen
also now collects the channel's metadata flow, so a ban or dissolution
landing mid-session flips the composer instead of waiting for a re-entry.

Only reasons a protocol can produce today are modeled. A Buzz tenant ban
(9040) or timeout (9042) is not among them: the relay decides at publish
time and reports it in an `OK false` no send path surfaces yet, so there is
no local state to derive it from. Nothing is claimed for archived Buzz
channels either — neither the relay contract nor our code refuses writes
there, so gating them would have hidden a composer the relay accepts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q3KXHjmCpUDzrSRyxatbyB
2026-07-30 02:10:08 +00:00
..
2026-03-26 14:35:18 -04:00
2026-01-06 06:49:59 +02:00