FolderDrawer: memoize label resolution, resolver early-return, fail-fast lookups #67

Closed
opened 2026-07-01 21:18:16 +00:00 by JMR-dev · 0 comments
JMR-dev commented 2026-07-01 21:18:16 +00:00 (Migrated from github.com)

Origin: code review of PR #54. Three small code-quality items in the drawer label plumbing, bundled because each is a few lines in the same two files.

  • Memoize label resolution in FolderDrawer. baseLabels/resolvedLabels (FolderDrawer.kt:82-83) are rebuilt on every recomposition of the drawer body — unlike the adjacent remember(folders)-cached sorted — and material3's ModalNavigationDrawer composes drawerContent even while closed. The fresh Map identity also defeats strong-skipping's memoization of each NavigationDrawerItem label lambda, so every folder tap re-executes all N rows instead of the ≤2 whose selection changed. Magnitude: sub-ms micro-waste, not jank. Fix: hoist the six role→string lookups once in composition (stringResource can't run inside remember's @DisallowComposableCalls calculation), then compute both maps inside remember(sorted, drawerAccount, roleStrings). Note: if label resolution moves to the ViewModel for #59, this item is subsumed.
  • Early-return in the resolver. resolveDrawerLabels always runs its full three passes even when duplicated is empty (the dominant case), rebuilding a map value-identical to baseLabels plus a second counting pass. Verified semantics-preserving one-liner: if (duplicated.isEmpty()) return baseLabels after FolderLabels.kt:38.
  • Fail-fast map lookups. Both ?: fallbacks — resolvedLabels[folder.fullName] ?: folderDisplayLabel(folder) (FolderDrawer.kt:92) and baseLabels[folder.fullName] ?: folder.displayName (FolderLabels.kt:37) — are provably dead (the maps are built from the exact list being iterated) and mutually inconsistent. If a future key change ever made them fire, the drawer would silently revert to un-deduplicated labels with all tests green. Use getValue(...) so a miss fails loudly.
Origin: code review of PR #54. Three small code-quality items in the drawer label plumbing, bundled because each is a few lines in the same two files. - [ ] **Memoize label resolution in `FolderDrawer`.** `baseLabels`/`resolvedLabels` (`FolderDrawer.kt:82-83`) are rebuilt on every recomposition of the drawer body — unlike the adjacent `remember(folders)`-cached `sorted` — and material3's `ModalNavigationDrawer` composes `drawerContent` even while closed. The fresh `Map` identity also defeats strong-skipping's memoization of each `NavigationDrawerItem` label lambda, so every folder tap re-executes all N rows instead of the ≤2 whose selection changed. Magnitude: sub-ms micro-waste, not jank. Fix: hoist the six role→string lookups once in composition (`stringResource` can't run inside `remember`'s `@DisallowComposableCalls` calculation), then compute both maps inside `remember(sorted, drawerAccount, roleStrings)`. Note: if label resolution moves to the ViewModel for #59, this item is subsumed. - [ ] **Early-return in the resolver.** `resolveDrawerLabels` always runs its full three passes even when `duplicated` is empty (the dominant case), rebuilding a map value-identical to `baseLabels` plus a second counting pass. Verified semantics-preserving one-liner: `if (duplicated.isEmpty()) return baseLabels` after `FolderLabels.kt:38`. - [ ] **Fail-fast map lookups.** Both `?:` fallbacks — `resolvedLabels[folder.fullName] ?: folderDisplayLabel(folder)` (`FolderDrawer.kt:92`) and `baseLabels[folder.fullName] ?: folder.displayName` (`FolderLabels.kt:37`) — are provably dead (the maps are built from the exact list being iterated) and mutually inconsistent. If a future key change ever made them fire, the drawer would silently revert to un-deduplicated labels with all tests green. Use `getValue(...)` so a miss fails loudly.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMail#67