fix: scroll mark-as-read crossing event missed by LazyVStack recycling
ROOT CAUSE: The exact crossing condition (oldMaxY >= 0 && newMaxY < 0) required onChange to fire at the exact moment maxY crosses zero. But LazyVStack recycles views when they scroll off-screen, destroying the GeometryReader before the crossing event is delivered. The entry goes from maxY=15 to being recycled — onChange never sees maxY go negative. FIX: Replace exact crossing with position check (newMaxY < 0). The entry just needs to be fully above the viewport. The other 5 guards prevent false positives: 1. trackingActive (scrolled past threshold) 2. isScrollingDown 3. !entry.isRead 4. !markedByScroll (dedup) 5. wasVisible (was >=50% visible) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -131,21 +131,24 @@ struct EntryListView: View {
|
||||
wasVisible.insert(entry.id)
|
||||
}
|
||||
}
|
||||
.onChange(of: frame.maxY) { oldMaxY, newMaxY in
|
||||
// Mark as read when entry scrolls above viewport.
|
||||
// All 6 conditions must be true:
|
||||
.onChange(of: frame.maxY) { _, newMaxY in
|
||||
// Mark as read when entry is above viewport.
|
||||
// 5 conditions must be true:
|
||||
// 1. Tracking active (scrolled past dynamic threshold)
|
||||
// 2. Scrolling downward
|
||||
// 3. Entry is unread
|
||||
// 4. Not already marked by scroll
|
||||
// 5. Was >=50% visible at some point (genuinely seen)
|
||||
// 6. Bottom edge just crossed above viewport
|
||||
//
|
||||
// We check newMaxY < 0 (entry fully above viewport)
|
||||
// instead of requiring an exact crossing event, because
|
||||
// LazyVStack can recycle the view between onChange calls,
|
||||
// causing the 0-boundary crossing to never be delivered.
|
||||
guard trackingActive,
|
||||
isScrollingDown,
|
||||
!entry.isRead,
|
||||
!markedByScroll.contains(entry.id),
|
||||
wasVisible.contains(entry.id),
|
||||
oldMaxY >= 0,
|
||||
newMaxY < 0 else { return }
|
||||
|
||||
markedByScroll.insert(entry.id)
|
||||
|
||||
Reference in New Issue
Block a user