From 5cfd1b4065d4283b99208e6c023bb717e9ce3deb Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Fri, 3 Jul 2026 09:56:08 -0500 Subject: [PATCH] perf(mailbox): bound paging window (maxSize) and memoize per-row timestamp Two fixes from the Paging 3 perf audit of the mailbox list (#212, #213): - Bound the unified-inbox paging window (#212): the only PagingConfig left maxSize at Int.MAX_VALUE, so pages were never evicted and a deep scroll accumulated the whole inbox in memory. Set maxSize = MAILBOX_PAGE_SIZE * 5 (200), satisfying maxSize >= pageSize + 2*prefetchDistance (120). Safe with enablePlaceholders = false (the UI null-guards evicted positions). - Memoize the per-row relative timestamp (#213): MessageRow formatted it via DateUtils.getRelativeTimeSpanString un-remembered, allocating a String on every recomposition. Wrapped in remember(timestampMillis). Co-Authored-By: Claude Opus 4.8 --- .../org/libremail/data/repository/MailRepositoryImpl.kt | 5 +++++ .../main/kotlin/org/libremail/ui/mailbox/MailboxScreen.kt | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/org/libremail/data/repository/MailRepositoryImpl.kt b/app/src/main/kotlin/org/libremail/data/repository/MailRepositoryImpl.kt index 9c30e20..4657638 100644 --- a/app/src/main/kotlin/org/libremail/data/repository/MailRepositoryImpl.kt +++ b/app/src/main/kotlin/org/libremail/data/repository/MailRepositoryImpl.kt @@ -97,6 +97,11 @@ class MailRepositoryImpl @Inject constructor( pageSize = MAILBOX_PAGE_SIZE, initialLoadSize = MAILBOX_PAGE_SIZE * 3, enablePlaceholders = false, + // Bound the in-memory window so a deep scroll can't accumulate the whole (potentially + // thousands-of-rows) inbox: keep ~5 pages resident and evict the rest. Must be + // >= pageSize + 2*prefetchDistance (40 + 2*40 = 120); with placeholders off, evicted leading + // positions drop from the loaded window and the list rendering already null-guards them. + maxSize = MAILBOX_PAGE_SIZE * 5, ), pagingSourceFactory = { messageDao.pagingUnifiedFolderSummaries(folder) }, ).flow.map { page -> page.map { it.toDomain() } } diff --git a/app/src/main/kotlin/org/libremail/ui/mailbox/MailboxScreen.kt b/app/src/main/kotlin/org/libremail/ui/mailbox/MailboxScreen.kt index a96c1ff..d095297 100644 --- a/app/src/main/kotlin/org/libremail/ui/mailbox/MailboxScreen.kt +++ b/app/src/main/kotlin/org/libremail/ui/mailbox/MailboxScreen.kt @@ -549,7 +549,9 @@ private fun MessageRow( Spacer(Modifier.width(4.dp)) } Text( - text = formatTimestamp(message.timestampMillis), + // remember so the relative-time string isn't re-formatted (allocating a new String + // and reading System.currentTimeMillis()) on every recomposition of the row. + text = remember(message.timestampMillis) { formatTimestamp(message.timestampMillis) }, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, )