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.
This commit is contained in:
Claude
2026-05-21 02:20:40 +00:00
parent 68f0ded7d5
commit 3915e2b36f
3 changed files with 48 additions and 24 deletions
@@ -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,
)
}
@@ -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()
}
}
@@ -32,10 +32,7 @@ import com.vitorpamplona.quartz.nip28PublicChat.base.IsInPublicChatChannel
class PublicChatsFeedFilter(
val account: Account,
) : AdditiveFeedFilter<Note>() {
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<Note>): List<Note> {
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<Note> =
compareByDescending<Note> {
it.idHex in followedSet
}.thenByDescending {
lastNote[it]
}.thenByDescending {
createdNote[it]