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 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 09:56:08 -05:00
co-authored by Claude Opus 4.8
parent 0e2be05be7
commit 5cfd1b4065
2 changed files with 8 additions and 1 deletions
@@ -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() } }
@@ -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,
)