From 3915e2b36f4dbf1d91e3fbf82c4eb6370a360d3e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 21 May 2026 02:20:40 +0000 Subject: [PATCH] refactor: render pinned followed chats as a separate LazyColumn section Instead of sorting followed chats to the top of the existing feed (which hides them when the feed filter or TopFilter excludes them), render them in a dedicated items{} block above the rest of the feed and skip them in the main itemsIndexed{} block. This keeps the feed filter, feedKey, and screen watchers untouched. --- .../publicChats/PublicChatsFeedLoaded.kt | 59 +++++++++++++++---- .../loggedIn/publicChats/PublicChatsScreen.kt | 4 +- .../publicChats/dal/PublicChatsFeedFilter.kt | 9 +-- 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/PublicChatsFeedLoaded.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/PublicChatsFeedLoaded.kt index 73872b432b..ce61cb95ae 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/PublicChatsFeedLoaded.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/PublicChatsFeedLoaded.kt @@ -23,14 +23,19 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.publicChats import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.HorizontalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState +import com.vitorpamplona.amethyst.model.LocalCache +import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -47,30 +52,58 @@ fun PublicChatsFeedLoaded( nav: INav, ) { val items by loaded.feed.collectAsStateWithLifecycle() + val followedSet by accountViewModel.account.publicChatList.flowSet + .collectAsStateWithLifecycle() + + val pinned = + remember(followedSet) { + followedSet.mapNotNull { idHex -> + LocalCache.getNoteIfExists(idHex)?.takeIf { it.event is ChannelCreateEvent } + } + } LazyColumn( contentPadding = rememberFeedContentPadding(FeedPadding), state = listState, ) { + items( + pinned, + key = { item -> "pinned-" + item.idHex }, + contentType = { item -> item.event?.kind ?: -1 }, + ) { item -> + PublicChatRow(item, accountViewModel, nav) + } + itemsIndexed( items.list, key = { _, item -> item.idHex }, contentType = { _, item -> item.event?.kind ?: -1 }, ) { _, item -> - Row(Modifier.fillMaxWidth().animateItem()) { - ChannelCardCompose( - baseNote = item, - routeForLastRead = "PublicChatsFeed", - modifier = Modifier.fillMaxWidth(), - forceEventKind = ChannelCreateEvent.KIND, - accountViewModel = accountViewModel, - nav = nav, - ) + if (item.idHex !in followedSet) { + PublicChatRow(item, accountViewModel, nav) } - - HorizontalDivider( - thickness = DividerThickness, - ) } } } + +@Composable +private fun LazyItemScope.PublicChatRow( + item: Note, + accountViewModel: AccountViewModel, + nav: INav, +) { + Row(Modifier.fillMaxWidth().animateItem()) { + ChannelCardCompose( + baseNote = item, + routeForLastRead = "PublicChatsFeed", + modifier = Modifier.fillMaxWidth(), + forceEventKind = ChannelCreateEvent.KIND, + accountViewModel = accountViewModel, + nav = nav, + ) + } + + HorizontalDivider( + thickness = DividerThickness, + ) +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/PublicChatsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/PublicChatsScreen.kt index ff2155f1ef..ae177d56d9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/PublicChatsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/PublicChatsScreen.kt @@ -106,10 +106,8 @@ fun WatchAccountForPublicChatsScreen( val hiddenUsers = accountViewModel.account.hiddenUsers.flow .collectAsStateWithLifecycle() - val followedChats by accountViewModel.account.publicChatList.flowSet - .collectAsStateWithLifecycle() - LaunchedEffect(accountViewModel, listState, hiddenUsers, followedChats) { + LaunchedEffect(accountViewModel, listState, hiddenUsers) { publicChatsFeedState.checkKeysInvalidateDataAndSendToTop() } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/dal/PublicChatsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/dal/PublicChatsFeedFilter.kt index 6932f2f88e..f55e184e26 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/dal/PublicChatsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/dal/PublicChatsFeedFilter.kt @@ -32,10 +32,7 @@ import com.vitorpamplona.quartz.nip28PublicChat.base.IsInPublicChatChannel class PublicChatsFeedFilter( val account: Account, ) : AdditiveFeedFilter() { - override fun feedKey(): String = - account.userProfile().pubkeyHex + "-" + followList().code + "-" + - account.publicChatList.flowSet.value - .hashCode() + override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + followList().code override fun limit() = 200 @@ -108,8 +105,6 @@ class PublicChatsFeedFilter( } override fun sort(items: Set): List { - val followedSet = account.publicChatList.flowSet.value - val lastNote = items.associateWith { note -> LocalCache.getPublicChatChannelIfExists(note.idHex)?.lastNote?.createdAt() ?: 0L @@ -122,8 +117,6 @@ class PublicChatsFeedFilter( val comparator: Comparator = compareByDescending { - it.idHex in followedSet - }.thenByDescending { lastNote[it] }.thenByDescending { createdNote[it]