mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-08 23:54:39 +00:00
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