From d9ba7187ca7123de20bc2eb08bfc60a90abd3314 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Apr 2026 15:17:51 +0000 Subject: [PATCH] fix(chats): make ChatroomLazyKey Bundle-storable LazyColumn keys are persisted in a SaveableStateHolder, which on Android must be Bundle-storable (primitives, Parcelable, or Serializable). The recent perf rework wrapped keys in plain data classes, which Kotlin does not auto-mark Serializable, so opening a public chat crashed with: java.lang.IllegalArgumentException: Type of the key PublicChannelLazyKey(channelId=...) is not supported. Mark the sealed interface Serializable, and decompose RoomId/ChatroomKey fields (which live in quartz commonMain and can't depend on the JVM-only Serializable interface) into String primitives. Each variant still wraps a stable per-chatroom identity, so reorders move rows instead of recreating them. --- .../chats/rooms/feed/ChatroomListFeedView.kt | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt index b852165f1..8db59aef7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt @@ -48,13 +48,12 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.ChatroomHeaderC 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.experimental.ephemChat.chat.RoomId import com.vitorpamplona.quartz.nip01Core.core.HexKey -import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey 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 +import java.io.Serializable @Composable fun ChatroomListFeedView( @@ -139,10 +138,11 @@ 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. Uses a -// sealed wrapper around an existing String/RoomId/ChatroomKey to avoid the -// StringBuilder + concatenation allocations of a typed-prefix string key. -private sealed interface ChatroomLazyKey +// message id, so reorders move the row instead of recreating it. Compose +// stores LazyColumn item keys in a SaveableStateHolder, which on Android +// requires Bundle-storable types, so each variant is Serializable and only +// holds primitives. +private sealed interface ChatroomLazyKey : Serializable private data class MarmotChatroomLazyKey( val groupId: HexKey, @@ -153,11 +153,12 @@ private data class PublicChannelLazyKey( ) : ChatroomLazyKey private data class EphemeralChannelLazyKey( - val roomId: RoomId, + val id: String, + val relayUrl: String, ) : ChatroomLazyKey private data class PrivateChatLazyKey( - val key: ChatroomKey, + val key: String, ) : ChatroomLazyKey private data class FallbackChatroomLazyKey( @@ -186,12 +187,12 @@ private fun chatroomLazyKey( } is EphemeralChatEvent -> { - event.roomId()?.let { EphemeralChannelLazyKey(it) } + event.roomId()?.let { EphemeralChannelLazyKey(it.id, it.relayUrl.url) } ?: FallbackChatroomLazyKey(item.idHex) } is ChatroomKeyable -> { - PrivateChatLazyKey(event.chatroomKey(myPubKey)) + PrivateChatLazyKey(event.chatroomKey(myPubKey).users.sorted().joinToString(",")) } else -> {