From 13c3ddd4469c54dba4e06b5b72dfe2bb969909b5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 16 May 2026 12:09:31 +0000 Subject: [PATCH] fix(chats): match public-channel rows by channel id when merging updates ChatroomListKnownFeedFilter.updateListWith only recognized ChannelMessageEvent when checking the old list for an existing row to replace. When a public channel's row was first populated from a ChannelCreateEvent or ChannelMetadataEvent (no message had arrived yet), the next incoming ChannelMessageEvent failed to match, so the filter appended a second note for the same channel. Both rows then produced the same PublicChannelLazyKey, crashing the LazyColumn with "Key was already used". Resolve the channel id from any IsInPublicChatChannel event (covers ChannelMessageEvent and ChannelMetadataEvent) and fall back to the event id for ChannelCreateEvent, mirroring how the lazy key is built in ChatroomListFeedView. --- .../rooms/dal/ChatroomListKnownFeedFilter.kt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListKnownFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListKnownFeedFilter.kt index 20699882cb..fff66acd53 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListKnownFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/dal/ChatroomListKnownFeedFilter.kt @@ -30,6 +30,8 @@ import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent import com.vitorpamplona.quartz.experimental.ephemChat.chat.RoomId 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.base.IsInPublicChatChannel import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent class ChatroomListKnownFeedFilter( @@ -111,7 +113,7 @@ class ChatroomListKnownFeedFilter( newRelevantPublicMessages.forEach { newNotePair -> var hasUpdated = false oldList.forEach { oldNote -> - val channelId = (oldNote.event as? ChannelMessageEvent)?.channelId() + val channelId = publicChannelIdOf(oldNote) if (newNotePair.key == channelId) { hasUpdated = true if ((newNotePair.value.createdAt() ?: 0L) > (oldNote.createdAt() ?: 0L)) { @@ -260,4 +262,16 @@ class ChatroomListKnownFeedFilter( } override fun sort(items: Set): List = items.sortedWith(DefaultFeedOrder) + + // The chat list keys public-channel rows by channel id, so the existing row + // for a channel may be backed by a ChannelCreateEvent or ChannelMetadataEvent + // (when no message has arrived yet) — not only a ChannelMessageEvent. Matching + // only ChannelMessageEvent here would leave the old row in place and append the + // new one, producing a duplicate LazyColumn key. + private fun publicChannelIdOf(note: Note): String? = + when (val event = note.event) { + is IsInPublicChatChannel -> event.channelId() + is ChannelCreateEvent -> event.id + else -> null + } }