mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 08:04:45 +00:00
feat: promote Relay Groups to a top-level destination + live rosters
Renames the feature to "Relay Groups" and makes it a first-class navigation destination rather than a Messages-tab sub-view, and keeps joined-group membership accurate app-wide. - Top-level Route.RelayGroups home (RelayGroupsHomeScreen): a root destination with the drawer top bar + bottom bar, listing the host relays of joined groups (each drilling into its channels) plus the discovery entry. Registered as NavBarItem.RELAY_GROUPS (Forum icon) in the drawer's Feeds section and pinnable to the bottom bar, with a roster preloader entry. - Naming: user-facing strings now say "group(s)" instead of "channel(s)"; added relay_groups_title = "Relay Groups". - Live rosters: RelayGroupRosterFilterAssembler keeps every joined group's 39000/39001/39002 fresh (one #d-scoped filter per host relay, re-derived from the join list) while a groups-bearing screen is on top. Mounted on both Messages panes and the new home, so membership, pending→member transitions and member counts stay accurate without opening each chat — the gap that most affected closed/private groups. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B5MLY4hq5LXJ2D5WeLRyXj
This commit is contained in:
+3
@@ -38,6 +38,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.datasource.Calend
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.datasource.ChatroomFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.datasource.ChannelFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupDirectoryFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupRosterFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.datasource.ChatroomListFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.datasource.ChessFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.datasource.CommunityFilterAssembler
|
||||
@@ -115,6 +116,7 @@ class RelaySubscriptionsCoordinator(
|
||||
// active depending on the screen.
|
||||
val channel = ChannelFilterAssembler(client)
|
||||
val relayGroupDirectory = RelayGroupDirectoryFilterAssembler(client)
|
||||
val relayGroupRoster = RelayGroupRosterFilterAssembler(client)
|
||||
val chatroom = ChatroomFilterAssembler(client)
|
||||
val community = CommunityFilterAssembler(client)
|
||||
val gitRepository = RepositoryFilterAssembler(client)
|
||||
@@ -177,6 +179,7 @@ class RelaySubscriptionsCoordinator(
|
||||
val all =
|
||||
listOf(
|
||||
relayGroupDirectory,
|
||||
relayGroupRoster,
|
||||
account,
|
||||
accountForeground,
|
||||
home,
|
||||
|
||||
@@ -109,6 +109,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayG
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.RelayGroupChannelListScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.RelayGroupChatScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.RelayGroupMembersScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.RelayGroupsHomeScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.MessagesScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.share.ShareToDMScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessGameScreen
|
||||
@@ -371,6 +372,7 @@ fun BuildNavigation(
|
||||
composableFromEnd<Route.Products> { ProductsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.Shorts> { ShortsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.PublicChats> { PublicChatsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.RelayGroups> { RelayGroupsHomeScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.FollowPacks> { FollowPacksScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.LiveStreams> { LiveStreamsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.Nests> { NestsScreen(accountViewModel, nav) }
|
||||
|
||||
+9
@@ -66,6 +66,7 @@ enum class NavBarItem {
|
||||
PODCAST_EPISODES,
|
||||
PODCASTS,
|
||||
PUBLIC_CHATS,
|
||||
RELAY_GROUPS,
|
||||
FOLLOW_PACKS,
|
||||
LIVE_STREAMS,
|
||||
NESTS,
|
||||
@@ -318,6 +319,13 @@ val NavBarCatalog: Map<NavBarItem, NavBarItemDef> =
|
||||
icon = MaterialSymbols.AutoMirrored.Chat,
|
||||
resolveRoute = { Route.PublicChats },
|
||||
),
|
||||
NavBarItem.RELAY_GROUPS to
|
||||
NavBarItemDef(
|
||||
id = NavBarItem.RELAY_GROUPS,
|
||||
labelRes = R.string.relay_groups_title,
|
||||
icon = MaterialSymbols.Forum,
|
||||
resolveRoute = { Route.RelayGroups },
|
||||
),
|
||||
NavBarItem.FOLLOW_PACKS to
|
||||
NavBarItemDef(
|
||||
id = NavBarItem.FOLLOW_PACKS,
|
||||
@@ -439,6 +447,7 @@ val DrawerFeedsItems: List<NavBarItem> =
|
||||
NavBarItem.NESTS,
|
||||
NavBarItem.COMMUNITIES,
|
||||
NavBarItem.PUBLIC_CHATS,
|
||||
NavBarItem.RELAY_GROUPS,
|
||||
NavBarItem.CALENDARS,
|
||||
NavBarItem.CALENDAR_COLLECTIONS,
|
||||
NavBarItem.SOFTWARE_APPS,
|
||||
|
||||
@@ -653,6 +653,8 @@ sealed class Route {
|
||||
val relayUrl: String,
|
||||
) : Route()
|
||||
|
||||
@Serializable object RelayGroups : Route()
|
||||
|
||||
@Serializable object RelayGroupBrowse : Route()
|
||||
|
||||
@Serializable data class ChannelMetadataEdit(
|
||||
|
||||
+3
@@ -29,6 +29,7 @@ import com.vitorpamplona.amethyst.ui.navigation.bottombars.NavBarItem
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.articles.datasource.ArticlesFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.badges.datasource.BadgesFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.datasource.CalendarsFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupRosterSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.datasource.ChatroomListFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.list.datasource.CommunitiesListFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.datasource.DiscoveryFilterAssemblerSubscription
|
||||
@@ -131,6 +132,8 @@ private fun PreloadFor(
|
||||
|
||||
NavBarItem.PUBLIC_CHATS -> PublicChatsFilterAssemblerSubscription(accountViewModel)
|
||||
|
||||
NavBarItem.RELAY_GROUPS -> RelayGroupRosterSubscription(accountViewModel.dataSources().relayGroupRoster, accountViewModel)
|
||||
|
||||
NavBarItem.FOLLOW_PACKS -> FollowPacksFilterAssemblerSubscription(accountViewModel)
|
||||
|
||||
NavBarItem.LIVE_STREAMS -> LiveStreamsFilterAssemblerSubscription(accountViewModel)
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.UserDrawerSearchTopBar
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupRosterSubscription
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
|
||||
/**
|
||||
* The top-level "Relay Groups" destination — one tap from the nav drawer / bottom
|
||||
* bar, distinct from the DM-centric Messages tab. Lists the host relays of the
|
||||
* groups the user has joined (each drilling into that relay's channels) and offers
|
||||
* the discovery entry to find more. Keeps every joined group's roster live while
|
||||
* on top via [RelayGroupRosterSubscription].
|
||||
*/
|
||||
@Composable
|
||||
fun RelayGroupsHomeScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
RelayGroupRosterSubscription(accountViewModel.dataSources().relayGroupRoster, accountViewModel)
|
||||
|
||||
DisappearingScaffold(
|
||||
isInvertedLayout = false,
|
||||
topBar = {
|
||||
UserDrawerSearchTopBar(accountViewModel, nav) {
|
||||
Text(
|
||||
text = stringRes(R.string.relay_groups_title),
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
}
|
||||
},
|
||||
bottomBar = {
|
||||
AppBottomBar(Route.RelayGroups, nav, accountViewModel) { route ->
|
||||
nav.navBottomBar(route)
|
||||
}
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) { padding ->
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
// Joined relays (each drills into its channels), followed by the
|
||||
// always-present "Find groups" discovery row.
|
||||
RelayGroupServerList(accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.service.relayClient.eoseManagers.PerUniqueIdEoseManager
|
||||
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||
import com.vitorpamplona.quartz.nip29RelayGroups.metadata.GroupAdminsEvent
|
||||
import com.vitorpamplona.quartz.nip29RelayGroups.metadata.GroupMembersEvent
|
||||
import com.vitorpamplona.quartz.nip29RelayGroups.metadata.GroupMetadataEvent
|
||||
|
||||
/** One screen's request to keep the roster of the user's joined groups fresh. */
|
||||
class RelayGroupRosterQueryState(
|
||||
val account: Account,
|
||||
)
|
||||
|
||||
/**
|
||||
* Roster kinds only (39000 metadata + 39001 admins + 39002 members) — enough to
|
||||
* resolve name, member count and this user's membership. Roles (39003) are pulled
|
||||
* by the per-chat / directory subscriptions when actually needed.
|
||||
*/
|
||||
private val RELAY_GROUP_ROSTER_KINDS =
|
||||
listOf(
|
||||
GroupMetadataEvent.KIND,
|
||||
GroupAdminsEvent.KIND,
|
||||
GroupMembersEvent.KIND,
|
||||
)
|
||||
|
||||
/**
|
||||
* Keeps the relay-signed roster (metadata/admins/members) of every group the user
|
||||
* has joined live while a groups-bearing screen is on top, so membership,
|
||||
* pending→member transitions and member counts stay accurate in list views
|
||||
* without having to open each chat. This matters most for closed/private groups,
|
||||
* where the only way to confirm a join was admitted is a fresh 39002.
|
||||
*
|
||||
* One subscription per host relay, scoped by `#d` to just that relay's joined
|
||||
* group ids — so we don't pull a relay's whole directory, only what we're in.
|
||||
*/
|
||||
class RelayGroupRosterFilterAssembler(
|
||||
client: INostrClient,
|
||||
) : ComposeSubscriptionManager<RelayGroupRosterQueryState>() {
|
||||
val group =
|
||||
listOf(
|
||||
RelayGroupRosterSubAssembler(client, ::allKeys),
|
||||
)
|
||||
|
||||
override fun invalidateKeys() = invalidateFilters()
|
||||
|
||||
override fun invalidateFilters() = group.forEach { it.invalidateFilters() }
|
||||
|
||||
override fun destroy() = group.forEach { it.destroy() }
|
||||
}
|
||||
|
||||
class RelayGroupRosterSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<RelayGroupRosterQueryState>,
|
||||
) : PerUniqueIdEoseManager<RelayGroupRosterQueryState, Account>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: RelayGroupRosterQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? {
|
||||
val joined = key.account.relayGroupList.liveRelayGroupList.value
|
||||
if (joined.isEmpty()) return null
|
||||
|
||||
// Group the joined group ids by their host relay: one #d-scoped filter each.
|
||||
val idsByRelay = joined.groupBy({ it.relayUrl }, { it.groupId })
|
||||
|
||||
return idsByRelay.mapNotNull { (relayUrl, groupIds) ->
|
||||
val relay = RelayUrlNormalizer.normalizeOrNull(relayUrl) ?: return@mapNotNull null
|
||||
RelayBasedFilter(
|
||||
relay = relay,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = RELAY_GROUP_ROSTER_KINDS,
|
||||
tags = mapOf("d" to groupIds.distinct()),
|
||||
since = since?.get(relay)?.time,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun id(key: RelayGroupRosterQueryState) = key.account
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
/**
|
||||
* Mount on any screen that lists the user's joined groups (the Messages tab's
|
||||
* inline/grouped views, the Relay Groups home) to keep their rosters live. The
|
||||
* assembler re-derives its per-relay filters from the joined-group list, so it
|
||||
* follows joins/leaves automatically.
|
||||
*/
|
||||
@Composable
|
||||
fun RelayGroupRosterSubscription(
|
||||
dataSource: RelayGroupRosterFilterAssembler,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
RelayGroupRosterQueryState(accountViewModel.account)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
}
|
||||
+5
@@ -43,6 +43,7 @@ import com.vitorpamplona.amethyst.ui.navigation.topbars.UserDrawerSearchTopBar
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.RelayGroupServerList
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.RelayGroupViewModeToggle
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupRosterSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.ChannelFabColumn
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.datasource.ChatroomListFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.feed.MessagesPager
|
||||
@@ -106,6 +107,10 @@ fun MessagesSinglePane(
|
||||
val viewMode by accountViewModel.account.settings.relayGroupViewMode
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
// Keep joined groups' rosters live while the messages list is on top, so
|
||||
// membership/pending state is accurate inline without opening each chat.
|
||||
RelayGroupRosterSubscription(accountViewModel.dataSources().relayGroupRoster, accountViewModel)
|
||||
|
||||
Column {
|
||||
RelayGroupViewModeToggle(accountViewModel)
|
||||
if (viewMode == RelayGroupViewMode.GROUPED) {
|
||||
|
||||
+3
@@ -53,6 +53,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.ChatroomVie
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.PublicChatChannelView
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.RelayGroupServerList
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.RelayGroupViewModeToggle
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupRosterSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.ChannelFabColumn
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20dp
|
||||
|
||||
@@ -102,6 +103,8 @@ fun MessagesTwoPane(
|
||||
val viewMode by accountViewModel.account.settings.relayGroupViewMode
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
RelayGroupRosterSubscription(accountViewModel.dataSources().relayGroupRoster, accountViewModel)
|
||||
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
RelayGroupViewModeToggle(accountViewModel)
|
||||
if (viewMode == RelayGroupViewMode.GROUPED) {
|
||||
|
||||
@@ -1929,22 +1929,23 @@
|
||||
<string name="messages_create_public_chat">Public</string>
|
||||
<string name="messages_create_group">Group</string>
|
||||
<string name="messages_create_public_private_chat_description">New Public or Private Group</string>
|
||||
<string name="relay_groups_title">Relay Groups</string>
|
||||
<string name="relay_group_view_inline">Inline</string>
|
||||
<string name="relay_group_view_grouped">By relay</string>
|
||||
<string name="relay_group_create_title">Create a channel</string>
|
||||
<string name="relay_group_create_name">Channel name</string>
|
||||
<string name="relay_group_create_title">Create a group</string>
|
||||
<string name="relay_group_create_name">Group name</string>
|
||||
<string name="relay_group_create_topic">Topic (optional)</string>
|
||||
<string name="relay_group_create_private">Private (members-only read)</string>
|
||||
<string name="relay_group_create_invite_only">Invite only</string>
|
||||
<string name="relay_group_create_confirm">Create</string>
|
||||
<string name="relay_group_invite_title">Invite people</string>
|
||||
<string name="relay_group_invite_description">Share this code so people can join this channel.</string>
|
||||
<string name="relay_group_invite_description">Share this code so people can join this group.</string>
|
||||
<string name="relay_group_invite_generating">Creating an invite…</string>
|
||||
<string name="relay_group_invite_copied">Invite code copied</string>
|
||||
<string name="relay_group_invite_code_label">Invite code (for this invite-only channel):</string>
|
||||
<string name="relay_group_join_title">Join channel</string>
|
||||
<string name="relay_group_invite_code_label">Invite code (for this invite-only group):</string>
|
||||
<string name="relay_group_join_title">Join group</string>
|
||||
<string name="relay_group_join_code_label">Invite code</string>
|
||||
<string name="relay_group_join_code_hint">This channel is invite-only. Enter the code you were given.</string>
|
||||
<string name="relay_group_join_code_hint">This group is invite-only. Enter the code you were given.</string>
|
||||
<string name="relay_group_join_confirm">Join</string>
|
||||
<string name="relay_group_pending">Requested</string>
|
||||
<string name="relay_group_role_admin">Admin</string>
|
||||
@@ -1954,18 +1955,18 @@
|
||||
<string name="relay_group_make_admin">Make admin</string>
|
||||
<string name="relay_group_make_moderator">Make moderator</string>
|
||||
<string name="relay_group_demote_member">Remove role</string>
|
||||
<string name="relay_group_remove_user">Remove from channel</string>
|
||||
<string name="relay_group_remove_user_confirm">Remove %1$s from this channel? They will lose access until re-added or re-invited.</string>
|
||||
<string name="relay_group_edit_title">Edit channel</string>
|
||||
<string name="relay_group_edit_name">Channel name</string>
|
||||
<string name="relay_group_remove_user">Remove from group</string>
|
||||
<string name="relay_group_remove_user_confirm">Remove %1$s from this group? They will lose access until re-added or re-invited.</string>
|
||||
<string name="relay_group_edit_title">Edit group</string>
|
||||
<string name="relay_group_edit_name">Group name</string>
|
||||
<string name="relay_group_edit_topic">Topic (optional)</string>
|
||||
<string name="relay_group_edit_private">Private (members-only read)</string>
|
||||
<string name="relay_group_edit_invite_only">Invite only</string>
|
||||
<string name="relay_group_edit_confirm">Save</string>
|
||||
<string name="relay_group_menu_members">Members</string>
|
||||
<string name="relay_group_menu_edit">Edit channel</string>
|
||||
<string name="relay_group_browse_title">Find channels</string>
|
||||
<string name="relay_group_browse_description">Browse the channels hosted on a group relay. Paste a relay address, or pick one below.</string>
|
||||
<string name="relay_group_menu_edit">Edit group</string>
|
||||
<string name="relay_group_browse_title">Find groups</string>
|
||||
<string name="relay_group_browse_description">Browse the groups hosted on a relay. Paste a relay address, or pick one below.</string>
|
||||
<string name="relay_group_browse_relay_label">Relay address</string>
|
||||
<string name="relay_group_browse_go">Browse</string>
|
||||
<string name="relay_group_browse_your_relays">Relays you\'re on</string>
|
||||
|
||||
Reference in New Issue
Block a user