perf(chats): typed sealed key instead of concatenated string
The previous fix used `"ch:${id}"`, `"dm:${users.sorted().joinToString}"`
etc., which allocates a StringBuilder + char[] + new String per call —
worst case for the DM branch which also allocates a sorted List on top.
Replace with a sealed `ChatroomLazyKey` and per-type data classes that
just wrap the existing String / RoomId / ChatroomKey. Equality and
hashCode are auto-generated, so Compose still moves rows correctly on
reorder, and we drop most of the per-key allocations:
ch:abc -> PublicChannelLazyKey(abc) # 1 wrapper, reused String
dm:userA,userB -> PrivateChatLazyKey(chatroomKey) # 1 wrapper, reused ChatroomKey
eph:roomId -> EphemeralChannelLazyKey(roomId) # 1 wrapper, reused RoomId
This commit is contained in:
+36
-9
@@ -48,7 +48,9 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.ChatroomHeaderC
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||||
import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent
|
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.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
|
||||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
||||||
@@ -137,38 +139,63 @@ private fun FeedLoaded(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stable per-chatroom key — derived from chatroom identity, not the latest
|
// Stable per-chatroom key — derived from chatroom identity, not the latest
|
||||||
// message id, so reorders move the row instead of recreating it.
|
// 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
|
||||||
|
|
||||||
|
private data class MarmotChatroomLazyKey(
|
||||||
|
val groupId: HexKey,
|
||||||
|
) : ChatroomLazyKey
|
||||||
|
|
||||||
|
private data class PublicChannelLazyKey(
|
||||||
|
val channelId: HexKey,
|
||||||
|
) : ChatroomLazyKey
|
||||||
|
|
||||||
|
private data class EphemeralChannelLazyKey(
|
||||||
|
val roomId: RoomId,
|
||||||
|
) : ChatroomLazyKey
|
||||||
|
|
||||||
|
private data class PrivateChatLazyKey(
|
||||||
|
val key: ChatroomKey,
|
||||||
|
) : ChatroomLazyKey
|
||||||
|
|
||||||
|
private data class FallbackChatroomLazyKey(
|
||||||
|
val noteIdHex: HexKey,
|
||||||
|
) : ChatroomLazyKey
|
||||||
|
|
||||||
private fun chatroomLazyKey(
|
private fun chatroomLazyKey(
|
||||||
item: Note,
|
item: Note,
|
||||||
myPubKey: HexKey,
|
myPubKey: HexKey,
|
||||||
): String {
|
): ChatroomLazyKey {
|
||||||
item.inGatherers
|
item.inGatherers
|
||||||
?.firstNotNullOfOrNull { it as? MarmotGroupChatroom }
|
?.firstNotNullOfOrNull { it as? MarmotGroupChatroom }
|
||||||
?.let { return "marmot:${it.nostrGroupId}" }
|
?.let { return MarmotChatroomLazyKey(it.nostrGroupId) }
|
||||||
|
|
||||||
return when (val event = item.event) {
|
return when (val event = item.event) {
|
||||||
is ChannelMessageEvent -> {
|
is ChannelMessageEvent -> {
|
||||||
"ch:${event.channelId() ?: item.idHex}"
|
PublicChannelLazyKey(event.channelId() ?: item.idHex)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ChannelMetadataEvent -> {
|
is ChannelMetadataEvent -> {
|
||||||
"ch:${event.channelId() ?: item.idHex}"
|
PublicChannelLazyKey(event.channelId() ?: item.idHex)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ChannelCreateEvent -> {
|
is ChannelCreateEvent -> {
|
||||||
"ch:${event.id}"
|
PublicChannelLazyKey(event.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
is EphemeralChatEvent -> {
|
is EphemeralChatEvent -> {
|
||||||
"eph:${event.roomId()?.toKey() ?: item.idHex}"
|
event.roomId()?.let { EphemeralChannelLazyKey(it) }
|
||||||
|
?: FallbackChatroomLazyKey(item.idHex)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ChatroomKeyable -> {
|
is ChatroomKeyable -> {
|
||||||
"dm:${event.chatroomKey(myPubKey).users.sorted().joinToString(",")}"
|
PrivateChatLazyKey(event.chatroomKey(myPubKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
item.idHex
|
FallbackChatroomLazyKey(item.idHex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user