mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-08 23:54:39 +00:00
feat(buzz): create channels/forums from section-label "+" instead of a FAB
On a Buzz community the FAB (which created a channel) is replaced by a "+" on the Channels label — matching how Direct Messages already offers a "+" — and a Forums label gains its own "+" that starts the create flow pre-set to a forum channel. Both section headers now always render so the "+" is reachable even before any channel loads; the collapse chevron is shown only when the section is non-empty. The create route gains an isForum flag (threaded onto RelayGroupCreateScreen → RelayGroupMetadataViewModel.isForum) so the Forums "+" opens the create screen on a forum, the Channels "+" on a chat channel. The vanilla NIP-29 relay (a flat directory with no sections) keeps its FAB. The stale "accept the invite in the browser" empty text is dropped — an empty community is now a starting point. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016MNVEKhaAu4vQRZnXv3rfG
This commit is contained in:
@@ -790,6 +790,7 @@ fun BuildNavigation(
|
||||
composableFromEndArgs<Route.RelayGroupCreate> {
|
||||
RelayGroupCreateScreen(
|
||||
relayUrl = it.relayUrl,
|
||||
isForum = it.isForum,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
|
||||
@@ -741,6 +741,9 @@ sealed class Route {
|
||||
|
||||
@Serializable data class RelayGroupCreate(
|
||||
val relayUrl: String,
|
||||
// Buzz only: start the create flow on a `forum` channel (threaded posts) instead of a
|
||||
// `stream` (chat) one — set by the community screen's per-section "+" buttons.
|
||||
val isForum: Boolean = false,
|
||||
) : Route()
|
||||
|
||||
@Serializable data class RelayGroupEdit(
|
||||
|
||||
+62
-26
@@ -338,13 +338,17 @@ fun RelayGroupChannelListScreen(
|
||||
}
|
||||
},
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(onClick = { nav.nav(Route.RelayGroupCreate(relay.url)) }, shape = CircleShape) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.Add,
|
||||
contentDescription =
|
||||
stringRes(if (isBuzz) R.string.buzz_channel_create_title else R.string.relay_group_create_title),
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
// A Buzz community creates channels/forums from the per-section "+" in their labels (like
|
||||
// Direct Messages), so no FAB there. A vanilla NIP-29 relay is a flat directory with no
|
||||
// sections, so it keeps the FAB to create a group.
|
||||
if (!isBuzz) {
|
||||
FloatingActionButton(onClick = { nav.nav(Route.RelayGroupCreate(relay.url)) }, shape = CircleShape) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.Add,
|
||||
contentDescription = stringRes(R.string.relay_group_create_title),
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
) { padding ->
|
||||
@@ -378,9 +382,11 @@ fun RelayGroupChannelListScreen(
|
||||
// overlays content by design, so clearing it is the list's job. As contentPadding (not a
|
||||
// modifier) so rows scroll *through* that strip and only come to rest clear of it; the
|
||||
// modifier form would shrink the viewport and leave the FAB floating over dead space.
|
||||
// Only the vanilla NIP-29 path has a FAB now; a Buzz community creates from its section
|
||||
// headers, so it needs no bottom clearance.
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(padding),
|
||||
contentPadding = PaddingValues(bottom = FAB_CLEARANCE),
|
||||
contentPadding = PaddingValues(bottom = if (isBuzz) 0.dp else FAB_CLEARANCE),
|
||||
) {
|
||||
if (showTorHint) {
|
||||
item(key = "tor-hint") {
|
||||
@@ -394,16 +400,15 @@ fun RelayGroupChannelListScreen(
|
||||
}
|
||||
|
||||
if (isBuzz) {
|
||||
// While the membership fetch is still running and nothing has loaded, show a
|
||||
// "Loading…" line. The old "you're not a member — accept the invite in the browser"
|
||||
// empty text is gone: the section labels below now each carry a "+" to create a
|
||||
// channel/forum, so an empty community is a starting point, not a dead end.
|
||||
val noChannelsYet = buzzChatChannels.isEmpty() && buzzForumChannels.isEmpty()
|
||||
if (noChannelsYet) {
|
||||
item(key = "buzz-no-channels") {
|
||||
if (noChannelsYet && buzzStatus is BuzzRelayImportViewModel.Status.Loading) {
|
||||
item(key = "buzz-loading") {
|
||||
Text(
|
||||
text =
|
||||
if (buzzStatus is BuzzRelayImportViewModel.Status.Loading) {
|
||||
stringRes(R.string.buzz_import_loading)
|
||||
} else {
|
||||
stringRes(R.string.buzz_import_empty_body)
|
||||
},
|
||||
text = stringRes(R.string.buzz_import_loading),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.fillMaxWidth().padding(24.dp),
|
||||
@@ -411,17 +416,24 @@ fun RelayGroupChannelListScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// -- CHANNELS -- (Add-all now lives in the community's top-bar overflow menu)
|
||||
if (buzzChatChannels.isNotEmpty()) {
|
||||
// -- 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.
|
||||
run {
|
||||
val channelsCollapsed = "channels" in collapsedSections
|
||||
item(key = "sec-channels") {
|
||||
RelayGroupSectionHeader(
|
||||
title = stringRes(R.string.relay_group_section_channels),
|
||||
collapsed = channelsCollapsed,
|
||||
onToggle = { toggleSection("channels") },
|
||||
)
|
||||
onToggle = if (buzzChatChannels.isNotEmpty()) ({ toggleSection("channels") }) else null,
|
||||
) {
|
||||
SectionAddButton(stringRes(R.string.buzz_channel_create_title)) {
|
||||
nav.nav(Route.RelayGroupCreate(relay.url))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!channelsCollapsed) {
|
||||
if (buzzChatChannels.isNotEmpty() && !channelsCollapsed) {
|
||||
itemsIndexed(buzzChatChannels, key = { _, it -> "chat-${it.id}" }) { index, groupId ->
|
||||
RowHairline(index)
|
||||
BuzzImportRow(
|
||||
@@ -438,17 +450,22 @@ fun RelayGroupChannelListScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// -- FORUMS --
|
||||
if (buzzForumChannels.isNotEmpty()) {
|
||||
// -- FORUMS -- Same treatment: an always-visible label with a "+" that starts the
|
||||
// create flow on a forum channel (threaded posts) instead of a chat one.
|
||||
run {
|
||||
val forumsCollapsed = "forums" in collapsedSections
|
||||
item(key = "sec-forums") {
|
||||
RelayGroupSectionHeader(
|
||||
title = stringRes(R.string.relay_group_section_forums),
|
||||
collapsed = forumsCollapsed,
|
||||
onToggle = { toggleSection("forums") },
|
||||
)
|
||||
onToggle = if (buzzForumChannels.isNotEmpty()) ({ toggleSection("forums") }) else null,
|
||||
) {
|
||||
SectionAddButton(stringRes(R.string.buzz_forum_create_title)) {
|
||||
nav.nav(Route.RelayGroupCreate(relay.url, isForum = true))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!forumsCollapsed) {
|
||||
if (buzzForumChannels.isNotEmpty() && !forumsCollapsed) {
|
||||
itemsIndexed(buzzForumChannels, key = { _, it -> "forum-${it.id}" }) { index, groupId ->
|
||||
RowHairline(index)
|
||||
BuzzImportRow(
|
||||
@@ -652,6 +669,25 @@ private fun RelayGroupSectionHeader(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The trailing "+" for a section label (Channels / Forums), matching the Direct Messages header's
|
||||
* New-message icon: a primary-tinted Add glyph that creates a new item of that section's type.
|
||||
*/
|
||||
@Composable
|
||||
private fun SectionAddButton(
|
||||
contentDescription: String,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
IconButton(onClick = onClick) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.Add,
|
||||
contentDescription = contentDescription,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* One inline Direct-Message conversation row inside the community view: the counterpart's avatar +
|
||||
* name (or a "+N" cluster label for a group DM), a preview of the last message, a compact
|
||||
|
||||
+6
-1
@@ -97,10 +97,15 @@ fun RelayGroupCreateScreen(
|
||||
relayUrl: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
// Buzz only: pre-select the `forum` channel type (the Forums section's "+" routes here with true).
|
||||
isForum: Boolean = false,
|
||||
) {
|
||||
val relay = remember(relayUrl) { RelayUrlNormalizer.normalizeOrNull(relayUrl) } ?: return
|
||||
val viewModel: RelayGroupMetadataViewModel = viewModel(key = "RelayGroupCreate:$relayUrl")
|
||||
LaunchedEffect(relay) { viewModel.initCreate(accountViewModel, relay) }
|
||||
LaunchedEffect(relay) {
|
||||
viewModel.initCreate(accountViewModel, relay)
|
||||
if (isForum) viewModel.isForum = true
|
||||
}
|
||||
|
||||
// A group only works if the relay actually runs NIP-29 (otherwise it stores our 9007/9002 as
|
||||
// ordinary events, never emits metadata/roster, and the "group" is a dead hex id). Gate creation
|
||||
|
||||
@@ -2570,6 +2570,7 @@
|
||||
<string name="relay_group_create_title">Create a group</string>
|
||||
<!-- Buzz calls its groups channels, and a Buzz relay is one workspace rather than a directory of groups. -->
|
||||
<string name="buzz_channel_create_title">New channel</string>
|
||||
<string name="buzz_forum_create_title">New forum</string>
|
||||
<string name="buzz_channel_flag_private">Private channel</string>
|
||||
<string name="buzz_channel_flag_private_desc">Hidden from the channel list and invite-only. Off means anyone on this relay can find and join it.</string>
|
||||
<string name="buzz_channel_flag_forum">Forum channel</string>
|
||||
|
||||
Reference in New Issue
Block a user