Skip to content

Scroll perf: drop hot-path logs, pure filter, cached results

jedmund requested to merge jedmund/scroll-perf into main

Created by: jedmund

Summary

Addresses the scroll-jank report in the illust detail view. From a code-review audit plus Time Profiler verification, three changes land here; the user confirms the trace no longer shows hiccups during detail-with-related-posts scroll.

  • Drop per-frame Logger calls on the cell hot path. IllustFeedCell.onGeometryChange fired heroLog.debug("cell onGeom id=… rect=…") every layout frame per visible cell — string-interpolating a formatted CGRect. CellRectRegistry.rect(for:) fired heroLog.notice(…) enhancement×/sec during dismiss-pan. Both gone. Can be re-added behind a compile-time flag if hero-transition debugging ever needs them back.
  • Remove body-level view-model writes. IllustFeedView.body was writing viewModel.mutes = MutesBridge.shared.current and viewModel.muteBehavior = muteBehavior every render. Replaced visibleIllusts computed with a pure filteredIllusts(behavior:mutes:) method the view calls with its own @AppStorage + MutesBridge reads. Pagination's loadMoreIfNeeded now takes visibleCount as an explicit parameter.
  • Cache the mute filter. MutesStore.version bumps on every mute/unmute/refresh; IllustFeedViewModel.illustsVersion bumps on every illusts mutation. filteredIllusts keys its cache on (illustsVersion, mutesVersion, mutes identity, behavior), so pure-scroll renders skip the O(n) walk entirely — filter only runs again when state actually changes.

Deferred (noted in the audit, not in this PR)

  • F2 — UIHostingController.sizingOptions = .intrinsicContentSize thrash in the related-posts nested grid. No longer suspected to be the primary jank source; will revisit only if a future regression points back to it.
  • Tier 2 — MasonryDistribution.distribute on column reflow, @AppStorage cell reads. Fine on current traces.
  • Tier 3 — thread-safety polish (explicit @MainActor on AuthSession tasks, TTLDataCache sweep cancel, SearchView task cancel on dismiss, BookmarksIndexObservable deinit cancel). Separate branch.

Test plan

  • Scroll Home rapidly; confirm frame-drops absent.
  • Open a detail with related posts, scroll into the related grid for several seconds — no hiccups.
  • Mute a user from a profile → feed drops them immediately.
  • Unmute from Settings → Muted Users → posts return on next load-more.
  • Toggle Settings → Mute behavior between Fully hide / Obscured → feed switches immediately.
  • Pull-to-refresh Home + Tag feed.
  • Bookmark/unbookmark a cell from the detail view → grid's heart state updates without re-fetch.
  • Hero push/pop animates correctly (removed CellRectRegistry logs were diagnostic, the animator's source-rect path is hot).

🤖 Generated with Claude Code

Merge request reports

Loading