fix: dedup emoji shortcodes in EmojiPackScreen to prevent LazyGrid crash

An emoji pack can carry duplicate shortcodes: NIP-30 puts no uniqueness
constraint on emoji tags, so foreign packs may repeat them and our own
addEmoji appends without a duplicate guard. The grid keyed items on
"${code}-${priv|pub}", so two same-code entries in the same visibility
bucket produced identical keys (e.g. "kohakucho-pub") and crashed the
LazyVerticalGrid with IllegalArgumentException.

Collapse to one cell per (shortcode, visibility) with distinctBy when
building the list. Beyond fixing the crash this is the correct UX: two
cells with the same shortcode are indistinguishable and share one delete
path (removeEmoji deletes by shortcode, dropping both). Dedup on code +
visibility so a legit public/private pair of the same shortcode survives.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01346aiAXBbdg5hMTAGydTqp
This commit is contained in:
Claude
2026-06-19 21:31:59 +00:00
parent a99e67d8be
commit 1371ba5d77
@@ -224,9 +224,19 @@ private fun EmojiGrid(
pack: OwnedEmojiPack,
onLongPress: (EmojiUrlTag, Boolean) -> Unit,
) {
// Collapse to one cell per (shortcode, visibility). A pack can carry
// duplicate shortcodes: NIP-30 puts no uniqueness constraint on emoji
// tags, so foreign packs may repeat them, and our own addEmoji appends
// without a duplicate guard. Two same-code cells in the same visibility
// bucket are indistinguishable to the user and share one delete path
// (removeEmoji deletes by shortcode, so it would drop both), and their
// identical keys would crash the LazyGrid. distinctBy keeps the first.
// Dedup on code + visibility so a legit public/private pair of the same
// shortcode (told apart by the lock badge, deleted separately) survives.
val allEmojis =
remember(pack) {
pack.publicEmojis.map { it to false } + pack.privateEmojis.map { it to true }
(pack.publicEmojis.map { it to false } + pack.privateEmojis.map { it to true })
.distinctBy { (emoji, isPrivate) -> emoji.code to isPrivate }
}
LazyVerticalGrid(