perf(note types): second pass — fix more missing remember keys

Follow-up to the first perf pass. Same goal: cut allocation cost for
note rows that scroll inside LazyColumn feeds.

- AppDefinition: key the `remember { tags.toImmutableListOfLists() }`
  block by `note` so it actually invalidates when the note changes
- NIP90ContentDiscoveryResponse: drop the `remember(note) {
  Modifier.fillMaxWidth() }` wrapper — `Modifier.fillMaxWidth()` is a
  constant call
- PeopleList: key the `derivedStateOf` for `name` by `noteEvent`, and
  switch `LaunchedEffect(Unit)` to `LaunchedEffect(noteEvent)` so the
  participants reload when the underlying event changes
- PinList: replace `val pins by remember { mutableStateOf(noteEvent
  .pinnedEvents()) }` with `val pins = remember(noteEvent) { … }` —
  the `mutableStateOf` wrapper was unnecessary and the missing key
  meant `pins` could go stale on event updates
- LongForm: return the `topics` list as `ImmutableList` so Compose
  treats it as a stable parameter to the consuming `forEach`
- RelayList: drop the `mutableStateOf(RelayListCard(…))` wrap inside
  4 `remember` blocks (DisplayRelaySet, DisplayNIP65RelayList write/
  read, DisplayDMRelayList) — the value never changes after creation;
  also key by `noteEvent` rather than `baseNote`, and cache
  `noteEvent.description()`
- Torrent: wrap `noteEvent.title() + totalSizeBytes()`, content
  comparison and `files().toImmutableList()` in `remember(noteEvent)`
  so they don't recompute and reallocate on every recomposition
This commit is contained in:
Claude
2026-04-26 15:34:22 +00:00
parent bf540db557
commit 6aecfe016b
7 changed files with 47 additions and 41 deletions
@@ -216,7 +216,7 @@ fun RenderAppDefinition(
CreateTextWithEmoji(
text = it,
tags =
remember {
remember(note) {
(note.event?.tags ?: emptyArray()).toImmutableListOfLists()
},
fontWeight = FontWeight.Bold,
@@ -64,6 +64,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size5dp
import com.vitorpamplona.amethyst.ui.theme.grayText
import com.vitorpamplona.amethyst.ui.theme.replyModifier
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
import kotlinx.collections.immutable.toImmutableList
private const val WORDS_PER_MINUTE = 225
private val COVER_ASPECT_RATIO = 16f / 9f
@@ -92,7 +93,14 @@ fun LongFormHeader(
remember(noteEvent) {
noteEvent.summary()?.ifBlank { null } ?: noteEvent.content.take(200).ifBlank { null }
}
val topics = remember(noteEvent) { noteEvent.topics().distinct().take(3) }
val topics =
remember(noteEvent) {
noteEvent
.topics()
.distinct()
.take(3)
.toImmutableList()
}
val readingMinutes = remember(noteEvent) { estimateReadingMinutes(noteEvent.content) }
Column(MaterialTheme.colorScheme.replyModifier) {
@@ -54,7 +54,6 @@ fun RenderNIP90ContentDiscoveryResponse(
note = note,
accountViewModel = accountViewModel,
) {
val modifier = remember(note) { Modifier.fillMaxWidth() }
val tags =
remember(note) { note.event?.tags?.toImmutableListOfLists() ?: EmptyTagList }
@@ -62,7 +61,7 @@ fun RenderNIP90ContentDiscoveryResponse(
content = noteEvent.content,
canPreview = canPreview && !makeItShort,
quotesLeft = quotesLeft,
modifier = modifier,
modifier = Modifier.fillMaxWidth(),
tags = tags,
backgroundColor = backgroundColor,
id = note.idHex,
@@ -81,7 +81,7 @@ fun DisplayPeopleList(
members.take(3)
}
val name by remember { derivedStateOf { "#${noteEvent.titleOrName() ?: noteEvent.dTag()}" } }
val name by remember(noteEvent) { derivedStateOf { "#${noteEvent.titleOrName() ?: noteEvent.dTag()}" } }
Text(
text = name,
@@ -95,7 +95,7 @@ fun DisplayPeopleList(
textAlign = TextAlign.Center,
)
LaunchedEffect(Unit) {
LaunchedEffect(noteEvent) {
accountViewModel.loadUsers(noteEvent.taggedUserIds()) {
members = it
}
@@ -65,7 +65,7 @@ fun RenderPinListEvent(
) {
val noteEvent = baseNote.event as? PinListEvent ?: return
val pins by remember { mutableStateOf(noteEvent.pinnedEvents()) }
val pins = remember(noteEvent) { noteEvent.pinnedEvents() }
var expanded by remember { mutableStateOf(false) }
@@ -75,12 +75,10 @@ fun DisplayRelaySet(
) {
val noteEvent = baseNote.event as? RelaySetEvent ?: return
val relays by
val relays =
remember(noteEvent) {
mutableStateOf(
RelayListCard(
noteEvent.relays().toImmutableList(),
),
RelayListCard(
noteEvent.relays().toImmutableList(),
)
}
@@ -89,10 +87,12 @@ fun DisplayRelaySet(
noteEvent.tags.firstTagValueFor("title", "name") ?: "#${noteEvent.dTag()}"
}
val description = remember(noteEvent) { noteEvent.description() }
DisplayRelaySet(
relays,
relayListName,
noteEvent.description(),
description,
backgroundColor,
accountViewModel,
nav,
@@ -108,21 +108,17 @@ fun DisplayNIP65RelayList(
) {
val noteEvent = baseNote.event as? AdvertisedRelayListEvent ?: return
val writeRelays by
remember(baseNote) {
mutableStateOf(
RelayListCard(
noteEvent.writeRelaysNorm() ?: emptyList(),
),
val writeRelays =
remember(noteEvent) {
RelayListCard(
noteEvent.writeRelaysNorm() ?: emptyList(),
)
}
val readRelays by
remember(baseNote) {
mutableStateOf(
RelayListCard(
noteEvent.readRelaysNorm() ?: emptyList(),
),
val readRelays =
remember(noteEvent) {
RelayListCard(
noteEvent.readRelaysNorm() ?: emptyList(),
)
}
@@ -154,12 +150,10 @@ fun DisplayDMRelayList(
) {
val noteEvent = baseNote.event as? ChatMessageRelayListEvent ?: return
val relays by
remember(baseNote) {
mutableStateOf(
RelayListCard(
noteEvent.relays(),
),
val relays =
remember(noteEvent) {
RelayListCard(
noteEvent.relays(),
)
}
@@ -153,19 +153,24 @@ fun RenderTorrent(
) {
val noteEvent = note.event as? TorrentEvent ?: return
val name = (noteEvent.title() ?: TorrentEvent.ALT_DESCRIPTION)
val size = " (" + countToHumanReadableBytes(noteEvent.totalSizeBytes()) + ")"
val description =
if (noteEvent.content != name) {
noteEvent.content
} else {
null
val title =
remember(noteEvent) {
val name = noteEvent.title() ?: TorrentEvent.ALT_DESCRIPTION
val size = " (" + countToHumanReadableBytes(noteEvent.totalSizeBytes()) + ")"
name + size
}
val description =
remember(noteEvent) {
val name = noteEvent.title() ?: TorrentEvent.ALT_DESCRIPTION
if (noteEvent.content != name) noteEvent.content else null
}
val files = remember(noteEvent) { noteEvent.files().toImmutableList() }
DisplayFileList(
noteEvent.files().toImmutableList(),
name + size,
files,
title,
description,
noteEvent::toMagnetLink,
backgroundColor,