fix(chats): stable per-chatroom LazyColumn key for messages list
The chatroom list keyed each row by `if (index == 0) index else item.idHex`. Two problems: 1. Position 0 was hardcoded to key `0`, so when a new chatroom moved to the top, the existing composition slot was reused with state from the previous chatroom — observed as "the row updated and reordered but still shows the old result" right at the top. 2. For other positions, the key was the latest message's `idHex`. When a new message arrived in any chatroom the chatroom's representative Note got replaced (different idHex), so Compose threw away the row and rebuilt it from scratch — wasted work. Fix: derive a stable key from chatroom identity instead of message id — nostr group id for marmot rooms, channel id for public/ephemeral channels, sorted user set for DMs. Falls back to `item.idHex` for unrecognized event types (drafts etc.). Reorders now move the row; new-message updates re-use the slot.
This commit is contained in:
+51
-4
@@ -25,14 +25,16 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.model.marmotGroups.MarmotGroupChatroom
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.feeds.FeedEmpty
|
||||
import com.vitorpamplona.amethyst.ui.feeds.FeedError
|
||||
@@ -45,6 +47,12 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.ChatroomHeaderCompose
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||
|
||||
@Composable
|
||||
fun ChatroomListFeedView(
|
||||
@@ -103,14 +111,16 @@ private fun FeedLoaded(
|
||||
) {
|
||||
val items by loaded.feed.collectAsStateWithLifecycle()
|
||||
|
||||
val myPubKey = accountViewModel.userProfile().pubkeyHex
|
||||
|
||||
LazyColumn(
|
||||
contentPadding = rememberFeedContentPadding(FeedPadding),
|
||||
state = listState,
|
||||
) {
|
||||
itemsIndexed(
|
||||
items(
|
||||
items.list,
|
||||
key = { index, item -> if (index == 0) index else item.idHex },
|
||||
) { _, item ->
|
||||
key = { item -> chatroomLazyKey(item, myPubKey) },
|
||||
) { item ->
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
ChatroomHeaderCompose(
|
||||
item,
|
||||
@@ -125,3 +135,40 @@ private fun FeedLoaded(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stable per-chatroom key — derived from chatroom identity, not the latest
|
||||
// message id, so reorders move the row instead of recreating it.
|
||||
private fun chatroomLazyKey(
|
||||
item: Note,
|
||||
myPubKey: HexKey,
|
||||
): String {
|
||||
item.inGatherers
|
||||
?.firstNotNullOfOrNull { it as? MarmotGroupChatroom }
|
||||
?.let { return "marmot:${it.nostrGroupId}" }
|
||||
|
||||
return when (val event = item.event) {
|
||||
is ChannelMessageEvent -> {
|
||||
"ch:${event.channelId() ?: item.idHex}"
|
||||
}
|
||||
|
||||
is ChannelMetadataEvent -> {
|
||||
"ch:${event.channelId() ?: item.idHex}"
|
||||
}
|
||||
|
||||
is ChannelCreateEvent -> {
|
||||
"ch:${event.id}"
|
||||
}
|
||||
|
||||
is EphemeralChatEvent -> {
|
||||
"eph:${event.roomId()?.toKey() ?: item.idHex}"
|
||||
}
|
||||
|
||||
is ChatroomKeyable -> {
|
||||
"dm:${event.chatroomKey(myPubKey).users.sorted().joinToString(",")}"
|
||||
}
|
||||
|
||||
else -> {
|
||||
item.idHex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user