mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 08:47:33 +00:00
feat(buzz): gate the channel/forum "+" to community members
Only a Buzz community member may create a channel (the relay makes the creator its owner; a non-member's kind-9007 is rejected), so the section-label "+" now shows only when the viewer is in the community's NIP-43 roster. Reactive: the relay-signed roster snapshot (kind 13534, republished on every membership change) can arrive after first composition, so BuzzCommunityMembership is re-read whenever one lands. The section labels themselves still render for everyone; only the "+" is gated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016MNVEKhaAu4vQRZnXv3rfG
This commit is contained in:
+43
-12
@@ -109,6 +109,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
|
||||
import com.vitorpamplona.quartz.nip29RelayGroups.GroupId
|
||||
import com.vitorpamplona.quartz.nip29RelayGroups.metadata.GroupMetadataEvent
|
||||
import com.vitorpamplona.quartz.nip43RelayMembers.list.RelayMembershipListEvent
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -208,6 +209,24 @@ fun RelayGroupChannelListScreen(
|
||||
// (kind-44100) so browsing the relay lists the channels you already belong to — each addable to
|
||||
// your kind-10009 list (so it then shows in Messages / Relay Groups). Same screen, one Browse.
|
||||
val isBuzz = BuzzRelayDialect.isBuzz(relay) || relayInfo.software?.contains("buzz", ignoreCase = true) == true
|
||||
|
||||
// Who may create a channel/forum here — the gate for the section "+" buttons. A Buzz relay lets
|
||||
// any community member create one (the creator becomes its owner); a non-member's 9007 is
|
||||
// rejected, so we only offer the "+" to members of this community's NIP-43 roster. Reactive: the
|
||||
// relay-signed roster snapshot (kind 13534, republished after every membership change) may land
|
||||
// after first composition, so re-read [BuzzCommunityMembership] whenever one does.
|
||||
val myPubkey = accountViewModel.account.signer.pubKey
|
||||
val canCreateChannels by produceState(
|
||||
initialValue = BuzzCommunityMembership.isMember(relay, myPubkey),
|
||||
relay,
|
||||
myPubkey,
|
||||
) {
|
||||
value = BuzzCommunityMembership.isMember(relay, myPubkey)
|
||||
LocalCache
|
||||
.observeEvents<RelayMembershipListEvent>(Filter(kinds = listOf(RelayMembershipListEvent.KIND)))
|
||||
.collect { value = BuzzCommunityMembership.isMember(relay, myPubkey) }
|
||||
}
|
||||
|
||||
val buzzVm: BuzzRelayImportViewModel = viewModel(key = "BuzzImport-${relay.url}")
|
||||
LaunchedEffect(relay, isBuzz) { if (isBuzz) buzzVm.bind(accountViewModel.account, relay.url) }
|
||||
val buzzChannels by buzzVm.channels.collectAsStateWithLifecycle()
|
||||
@@ -352,7 +371,6 @@ fun RelayGroupChannelListScreen(
|
||||
}
|
||||
},
|
||||
) { padding ->
|
||||
val myPubkey = accountViewModel.userProfile().pubkeyHex
|
||||
// A Buzz relay is a community, so always render its sectioned list (Channels, Forums, Direct
|
||||
// Messages, Agent Console) even before anything loads, rather than the generic "empty" text.
|
||||
if (channels.isEmpty() && !isBuzz) {
|
||||
@@ -419,7 +437,8 @@ fun RelayGroupChannelListScreen(
|
||||
// -- CHANNELS -- The label carries a "+" to create a channel (the community's FAB
|
||||
// moved here, like Direct Messages). Add-all lives in the top-bar overflow menu.
|
||||
// The header always shows so the "+" is available even before any channel loads;
|
||||
// the collapse toggle is offered only when there's something to collapse.
|
||||
// the collapse toggle is offered only when there's something to collapse. The "+"
|
||||
// is offered only to community members, who are the ones the relay lets create.
|
||||
run {
|
||||
val channelsCollapsed = "channels" in collapsedSections
|
||||
item(key = "sec-channels") {
|
||||
@@ -427,11 +446,17 @@ fun RelayGroupChannelListScreen(
|
||||
title = stringRes(R.string.relay_group_section_channels),
|
||||
collapsed = channelsCollapsed,
|
||||
onToggle = if (buzzChatChannels.isNotEmpty()) ({ toggleSection("channels") }) else null,
|
||||
) {
|
||||
SectionAddButton(stringRes(R.string.buzz_channel_create_title)) {
|
||||
nav.nav(Route.RelayGroupCreate(relay.url))
|
||||
}
|
||||
}
|
||||
trailing =
|
||||
if (canCreateChannels) {
|
||||
{
|
||||
SectionAddButton(stringRes(R.string.buzz_channel_create_title)) {
|
||||
nav.nav(Route.RelayGroupCreate(relay.url))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
if (buzzChatChannels.isNotEmpty() && !channelsCollapsed) {
|
||||
itemsIndexed(buzzChatChannels, key = { _, it -> "chat-${it.id}" }) { index, groupId ->
|
||||
@@ -459,11 +484,17 @@ fun RelayGroupChannelListScreen(
|
||||
title = stringRes(R.string.relay_group_section_forums),
|
||||
collapsed = forumsCollapsed,
|
||||
onToggle = if (buzzForumChannels.isNotEmpty()) ({ toggleSection("forums") }) else null,
|
||||
) {
|
||||
SectionAddButton(stringRes(R.string.buzz_forum_create_title)) {
|
||||
nav.nav(Route.RelayGroupCreate(relay.url, isForum = true))
|
||||
}
|
||||
}
|
||||
trailing =
|
||||
if (canCreateChannels) {
|
||||
{
|
||||
SectionAddButton(stringRes(R.string.buzz_forum_create_title)) {
|
||||
nav.nav(Route.RelayGroupCreate(relay.url, isForum = true))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
if (buzzForumChannels.isNotEmpty() && !forumsCollapsed) {
|
||||
itemsIndexed(buzzForumChannels, key = { _, it -> "forum-${it.id}" }) { index, groupId ->
|
||||
|
||||
Reference in New Issue
Block a user