Skip to content

Let community admins set default channels - #2468

Open
TheSentinel454 wants to merge 2 commits into
mainfrom
elrond/default-channel-toggle
Open

TheSentinel454 wants to merge 2 commits into
mainfrom
elrond/default-channel-toggle

Conversation

@TheSentinel454

Copy link
Copy Markdown
Contributor

Summary

Community owners and admins can make a channel a default channel from the app: Members → General access gets a "Default channel" switch on open, unarchived stream channels. The relay then joins every new community member to it, with the channel's join role (Member or Guest).

The relay already supported this (kind:9002 ["auto_join", "true"|"false"], community owner/admin only, open and unarchived streams only — NOSTR.md § Default channels in block/buzz). Until now it was only reachable from the relay CLI.

How it works

  • Separate, narrow command. The default-channel setting is sent on its own as ["h", <channel>] + ["auto_join", ...], never combined with a details save. A community admin is not necessarily a channel admin, and the details editor always resends name/about, which needs channel admin. Both the TS broker validator and the native Rust validator admit exactly that two-tag shape for route channel-details and reject it combined with anything else.
  • Role comes from the relay. The switch shows only for community owners/admins per the relay-signed kind:13534 roster (useCommunityRole), and the write re-reads the channel before sending.
  • Confirmed, not optimistic. The switch changes only after the relay's kind:39000 readback shows the bare ["auto_join"] tag in the new state. An accepted write that does not read back stays in the existing uncertain state and cannot be resubmitted until refreshed.
  • Join role (General access: who can post) already existed in this dialog; the switch sits next to it.

What got simpler

Nothing removed. The change reuses the existing General access flow, details protocol and readback confirmation rather than adding a second write path or store.

Tests

  • channel-details.test.ts: the narrow command's exact tag shape, validator rejection of combined or malformed commands, authorization by community role rather than channel role, and readback confirmation (including a private channel whose readback carries no default-channel state).
  • ChannelGeneralAccess.test.tsx: switch visibility by community role and channel eligibility, toggling on and off through the real flow, and the unconfirmed path.
  • channel_writes.rs: details_admit_only_the_exact_default_channel_command.

Gated on a Blox workstation: biome, icon check, rustfmt, check-push.mjs (types + related unit tests, design system, clippy), focused Vitest, and cargo test relay::channel_writes.

🤖 Generated with Claude Code

Members -> General access gets a Default channel switch for community
owners and admins on open, unarchived stream channels. It sends the
relay's narrow kind:9002 auto_join command on its own and changes only
after the channel's kind:39000 readback confirms it.

Signed-off-by: Luke Tornquist <tornquist@squareup.com>
Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@TheSentinel454
TheSentinel454 marked this pull request as ready for review November 4, 2026 18:54
@TheSentinel454
TheSentinel454 requested a review from a team as a code owner November 4, 2026 18:54
A default-channel attempt now remembers that it is one and confirms only
when the readback shows the requested value; archived and private
metadata keep the change uncertain. A failed community role read shows
an error with a retry instead of hiding the setting silently.

Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Luke Tornquist <tornquist@squareup.com>

@kazemek21 kazemek21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kyle reviewed the PR and the Nexus review-tracker notes and approved with the two non-blocking suggestions below.

Posted by Codex (gpt-6.1-sol) on behalf of kylez

</Button>
</div>
)}
{base?.autoJoin !== undefined && !solo && community.manager && (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve default-channel management for community admins with read-only access

Should this control remain available when a community admin is a channel Guest or nonmember? discovery.get() marks Guests/previews read-only, so the early return hides it; nonmembers also fail lifecycleSettings(). The relay accepts this command based on community role without requiring channel membership. Please separate that management path from channel posting/edit rights.

Posted by Codex (gpt-6.1-sol) on behalf of kylez

(draft.joinRole !== undefined && details.joinRole !== draft.joinRole) ||
// Other saves never send the setting. Archived and private channels
// keep it without reporting it, so missing state proves nothing.
(defaultChange && details.autoJoin !== draft.autoJoin) ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Confirm only the default state for a default-only command

If another admin renames the channel between the last authorization read and confirmation, the toggle can succeed but this comparison leaves it permanently unconfirmed. check() repeats the old name comparison and attempts.has(id) prevents another save. Should the defaultChange branch confirm only the requested autoJoin state, while retaining the existing full-details checks for ordinary saves?

Posted by Codex (gpt-6.1-sol) on behalf of kylez

@wpfleger96 wpfleger96 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Automated review of head 3b74e39ed5f9d31bcda94dad059ddafe68cb12a8.

IMPORTANT: main already ships this feature. #1624 (merged 2026-11-05, 84224a52c7) added a "Starter channel for new members" switch to ChannelSettingsPanel.tsx, shown only to community owners and admins (managesCommunity(role)) on active public stream channels. It sends the same two-tag kind 9002 [["h", id], ["auto_join", "true"|"false"]] through the lifecycle route (auto-join-on / auto-join-off in channel-lifecycle-protocol.ts), and both host validators already admit that shape (validateLifecycleTemplate and channel_writes.rs lifecycle). It also confirms from the republished 39000 and parses the bare auto_join tag into ChannelSummary.autoJoin.

This PR adds a second control for the same relay setting in Members → General access and a second admission path for the identical command on the channel-details route (defaultTemplate in channel-details-protocol.ts, the early return in details() in channel_writes.rs). Rebased onto main as-is, admins would see two switches for one setting with different names ("Default channel" and "Starter channel for new members") and different save, confirmation and error paths, and the docs would describe the same setting twice under two names. It is also why the PR conflicts with main in ChannelGeneralAccess.tsx, which now warns about starter channels when one is made private.

Fix: close this PR in favor of #1624. Or, if something here should survive, rebase onto main and keep only that change on top of the existing lifecycle command and ChannelSummary.autoJoin, without the parallel channel-details path. The one difference I found is the join-role wording ("join automatically as guests/members").

Smaller note on the same flow (only matters if any of this survives): a community admin who is not a member of the channel gets the read-only preview in the members dialog, so they never see the switch here, even though the relay lets them set it without membership.

@morgmart

morgmart commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

🤖 Design review (critical path: new-member onboarding)

Looks good from a design standpoint. No must-fix items.

Optional:

  • After switching Default channel, the status reads "Access updated". "Default channel updated" would match what the person just did; the saving message already says "Saving default channel…".
  • The switch says new members join "as guests", but the access menu right above calls that "View only". Using the same words ("join and can only view") avoids a second name for the same thing.
  • Making a default channel private or archiving it hides the switch but keeps the setting, so reopening the channel silently makes it a default again. Consider clearing it on those changes, or saying so where the admin makes them.
  • The "Couldn't check your community role" error can show to channel admins who aren't community admins and would never get the switch. Showing it only when the person might be a manager would avoid a confusing alert.

Not checked: no screenshots in the PR and I didn't run the app, so I haven't seen the row in light/dark, narrow windows, or enlarged text, or what a new member sees on first join.

{base?.autoJoin !== undefined && !solo && community.manager && (
<SwitchPreferenceRow
label="Default channel"
description={`New community members join automatically as ${base.joinRole === "guest" ? "guests" : "members"}.`}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Please use View only for the guest case, matching the General access menu (generalAccessOptions["view-only"].label). For example: "New community members join automatically with View only access." Calling the same access mode "guests" here makes it sound like a different setting.

Comment on lines +348 to +355
{base?.autoJoin !== undefined &&
!solo &&
!community.manager &&
community.readError && (
<div className={styles.accessFeedback}>
<p role="alert" className="text-body-sm text-danger">
Couldn’t check your community role, so the default channel
setting is unavailable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Please keep this warning and Retry action hidden for channel admins who are not community owners/admins. !community.manager && community.readError also matches a failed roster lookup for those users, even though they would never be offered the Default channel switch. They should not see an alert about a setting they cannot use when their channel access is unaffected. Please scope the role-check recovery to users for whom this control is relevant, and add regression coverage for a channel admin who is only a community member when the role lookup fails.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants