Moderation U1: disable composer in a moderation DM (member surface)#1614
Open
tlongwell-block wants to merge 2 commits into
Open
Moderation U1: disable composer in a moderation DM (member surface)#1614tlongwell-block wants to merge 2 commits into
tlongwell-block wants to merge 2 commits into
Conversation
A moderation DM is the 1:1 thread between a member and the relay's own identity, where moderators explain an action. The member must not be able to reply into it, so the composer is read-only there — and only there, never on ordinary DMs. The relay identity is the NIP-11 `self` pubkey. A private helper for this already existed in commands/identity_archive.rs (fetch_relay_self: unauthenticated NIP-11 GET, 64-hex validation, fail-open); rather than add a second copy, promote it to pub(crate) and expose it through one thin `get_relay_self` command, so the archive read and the moderation read share a single source of truth. - commands/identity_archive.rs: fetch_relay_self → pub(crate); add the get_relay_self command (+ register in lib.rs). - features/moderation/lib/relaySelf.ts: getRelaySelf() invoke wrapper; hooks.ts: useRelaySelfQuery (workspace-scoped, cached indefinitely, null is a valid fail-open answer). - features/moderation/lib/moderationDm.ts (+ tests): isModerationDm — pure predicate, true only for a DM whose sole other participant is the relay self. Case-insensitive; fails open on a null/absent self. - ChannelPane: query self only for DM channels, fold isModerationDm into the shared isComposerDisabled (thread panel inherits), and show a read-only placeholder. The moderator's message stays visible; only the reply is blocked. Affordance, not enforcement: the relay is authoritative over writes, so a false negative (composer stays enabled on a real moderation DM) is the safe failure — the relay rejects the write anyway. A false positive (disabling a normal DM) is the worse outcome, so every uncertain path resolves to "enabled". Validated: tsc --noEmit clean, biome clean, moderation tests 15/15 green, src-tauri cargo check clean. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
The report-queue timeout resolution (U2) needs the same 1h/24h/7d duration choice the per-message author cluster already offers, and the two must never drift. Provide the shared piece from the timeout lib that owns the parse/format side. - lib/timeout.ts: TIMEOUT_PRESETS (the single 1h/24h/7d source of truth) and timeoutExpiresAt(seconds) → now + seconds in epoch seconds, the shape useTimeoutMemberMutation expects. (+ tests.) - ui/TimeoutDurationSubmenu.tsx: a presentational DropdownMenuSub of those presets. Duration-only — it knows nothing about the target or which command fires — so it drops into both the per-message cluster and the queue's timeout resolution unchanged, calling onSelect with the absolute expiry. No consumer on this branch yet; U2 wires it into the resolve menu's timeout branch. Validated: tsc --noEmit clean, biome clean, moderation tests 17/17 green. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
cfc415c to
d554db8
Compare
171d15f to
c1c81d0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lane U1 (member surface) for Phase-2 community moderation, stacked on the integration branch
eva/moderation-ui.The bulk of U1 — report modal, tombstone render, timeout parser + chip, and the shared type-flip fix — already reached integration (commits
06572210,7295cff2,6ebe3b91). This PR is the last U1 piece: disabling the composer in a moderation DM.What this adds
A moderation DM is the 1:1 thread between a member and the relay's own identity, where moderators explain an action. The member must not be able to reply into it, so the composer is read-only there — and only there, never on ordinary DMs.
get_relay_selfcommand — the relay identity is the NIP-11selfpubkey. A privatefetch_relay_selfhelper already existed incommands/identity_archive.rs(unauthenticated NIP-11 GET, 64-hex validation, fail-open); rather than a second copy, it's promoted topub(crate)and exposed through one thin command, so the archive read and the moderation read share a single source of truth.getRelaySelf/useRelaySelfQuery— workspace-scoped, cached indefinitely;nullis a valid fail-open answer, not an error to retry.isModerationDm(+ 7 tests) — pure predicate, true only for a DM whose sole other participant is the relay self. Case-insensitive; fails open on a null/absent self.ChannelPane— queriesselfonly for DM channels, folds the result into the sharedisComposerDisabled(thread panel inherits the block), and shows a read-only placeholder. The moderator's message stays visible; only the reply is blocked.Fail-open, by design
It's an affordance, not enforcement — the relay is authoritative over writes. A false negative (composer stays enabled on a real moderation DM) is the safe failure; the relay rejects the write anyway. A false positive (disabling a normal DM) is the worse outcome, so every uncertain path resolves to "enabled".
Validation
tsc --noEmitclean · biome clean · moderation tests 15/15 green ·src-tauricargo check clean · all pre-push legs (rust-tests, desktop-test, mobile-test, desktop-tauri-test) green.DCO: authored by agent npub,
Signed-off-by+Co-authored-by: Tyler Longwell <tlongwell@block.xyz>.