Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Strand/Liquid/LiquidTodayView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,8 @@ struct LiquidTodayView: View {

liquidRefreshIndicator // grows in the revealed space; a vessel filling with the pull

VStack(alignment: .leading, spacing: 12) {
// Materialise charts below the viewport only when they approach the visible area.
LazyVStack(alignment: .leading, spacing: NoopMetrics.space3) {
scene
// The strain/illness early-warning banner, dropped in the liquid Home rewrite. Liquid is
// the DEFAULT Today on both platforms (RootTabView.swift's liquidTodayEnabled = true,
Expand Down
7 changes: 2 additions & 5 deletions Strand/Screens/TodayView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1455,10 +1455,7 @@ struct TodayView: View {

var body: some View {
ScreenScaffold(title: scaffoldTitle, onRefresh: { await repo.refresh() },
// PERF (scroll): lazy column so the scaffold materialises Today's content on demand.
// Today supplies its own inner eager VStack (below), so the staggered section reveal is
// unchanged, this only defers building the single inner stack until it scrolls in.
// Byte-identical layout (LazyVStack == eager VStack alignment/spacing/header).
// Both columns are lazy so off-screen sections defer chart layout.
lazy: true,
// PERF (scroll stutter): the day-cycle scene is a static masked Image. CoreAnimation
// already caches it as a stable image layer, so it does NOT re-rasterize on body
Expand All @@ -1468,7 +1465,7 @@ struct TodayView: View {
// lag regression; removing the flatten restores native layer caching.
topBackground: showDayCycleBackground
? AnyView(SceneScreenBackground(hour: demoSceneHour)) : nil) {
VStack(alignment: .leading, spacing: NoopMetrics.sectionGap) {
LazyVStack(alignment: .leading, spacing: NoopMetrics.sectionGap) {
#if os(iOS)
// Compact top bar: profile/settings (left) · ‹ Today › day-nav (centre, bold) · strap
// battery (right). Replaces the big title + the full-width day-nav pill (WHOOP-style).
Expand Down
16 changes: 3 additions & 13 deletions Strand/Screens/TrendsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -291,9 +291,7 @@ struct TrendsView: View {

private var scaffold: some View {
ScreenScaffold(title: "Trends", subtitle: "The thread of you over time.",
// PERF (scroll): lazy column — byte-identical layout (LazyVStack == eager VStack
// alignment/spacing/header). The content is one inner eager VStack, so the staggered
// section reveal is unchanged; this only defers building that stack until it scrolls in.
// Defer off-screen charts in the inner column as well as the scaffold.
onRefresh: { await repo.refresh() },
lazy: true,
topBackground: liquidScaffoldSky()) {
Expand All @@ -305,31 +303,23 @@ struct TrendsView: View {
// Reuse the resolved windows until the data, range, or loaded Rest series changes.
// An unrelated Repository publication must not re-filter five years of history.
let metrics = resolvedMetrics
VStack(alignment: .leading, spacing: NoopMetrics.sectionSpacing) {
// The main card list ripples in once on appear (Reduce-Motion safe).
LazyVStack(alignment: .leading, spacing: NoopMetrics.sectionSpacing) {
// Charts enter directly; lazy scrolling must not schedule staggered layout animations.
Group {
// Week-in-review digest (#208) with prev/next week browsing (#710) — self-hides
// only when NO week in history has data. Past weeks render in the same format.
weeklyDigestNav
.staggeredAppear(index: 0)
// The Charge / Effort / Rest trio, presented in NOOP's pip language.
weekInReview(charge: metrics.recovery, effort: metrics.strain, rest: metrics.rest)
.staggeredAppear(index: 1)
rangeBar(recovery: metrics.recovery)
.staggeredAppear(index: 2)
heroRecovery(recovery: metrics.recovery)
.staggeredAppear(index: 3)
smallMultiples(hrv: metrics.hrv, rhr: metrics.rhr, strain: metrics.strain)
.staggeredAppear(index: 4)
// Long-horizon training load (CTL/ATL/TSB). Uses the FULL history, not the
// range window — chronic load is inherently a 42-day horizon. Self-hides its
// chart behind an honest "needs N more days" state until enough history exists.
TrainingLoadCard(days: repo.days)
.staggeredAppear(index: 5)
yearStrip
.staggeredAppear(index: 6)
exportReportRow
.staggeredAppear(index: 7)
}
}
}
Expand Down
Loading