debug: Reader comprehensive logging — jitter, markRead, loadMore
All checks were successful
Security Checks / dependency-audit (push) Successful in 13s
Security Checks / secret-scanning (push) Successful in 4s
Security Checks / dockerfile-lint (push) Successful in 4s

This commit is contained in:
Yusuf Suleman
2026-04-04 23:06:21 -05:00
parent 8a04c18627
commit dbddf126f9
3 changed files with 23 additions and 2 deletions

View File

@@ -94,15 +94,19 @@ final class ReaderViewModel {
func loadMore() async { func loadMore() async {
guard !isLoadingMore, hasMore else { return } guard !isLoadingMore, hasMore else { return }
isLoadingMore = true isLoadingMore = true
print("[READER-DBG] 📥 loadMore START offset=\(offset)")
do { do {
let list = try await fetchEntries(offset: offset) let list = try await fetchEntries(offset: offset)
let count = list.entries.count
entries.append(contentsOf: list.entries) entries.append(contentsOf: list.entries)
total = list.total total = list.total
offset += list.entries.count offset += count
hasMore = offset < list.total hasMore = offset < list.total
print("[READER-DBG] 📥 loadMore END +\(count) total=\(entries.count) hasMore=\(hasMore)")
} catch { } catch {
self.error = error.localizedDescription self.error = error.localizedDescription
print("[READER-DBG] 📥 loadMore ERROR: \(error)")
} }
isLoadingMore = false isLoadingMore = false
} }

View File

@@ -123,6 +123,7 @@ struct EntryListView: View {
frame.maxY < 30 else { return } frame.maxY < 30 else { return }
markedByScroll.insert(entryId) markedByScroll.insert(entryId)
print("[READER-DBG] 📖 markRead id=\(entryId) autoScroll=\(isAutoScrolling) maxY=\(Int(frame.maxY))")
if isAutoScrolling { if isAutoScrolling {
// Defer visual update contentSize changes cause jitter // Defer visual update contentSize changes cause jitter

View File

@@ -98,20 +98,36 @@ struct ScrollViewDriver: UIViewRepresentable {
displayLink = nil displayLink = nil
} }
private var lastTickTime: CFAbsoluteTime = 0
private var lastContentSize: CGFloat = 0
private var tickCount = 0
@objc private func tick(_ link: CADisplayLink) { @objc private func tick(_ link: CADisplayLink) {
guard let sv = scrollView else { guard let sv = scrollView else {
stopAndNotify() stopAndNotify()
return return
} }
let now = CFAbsoluteTimeGetCurrent()
let maxOffset = sv.contentSize.height - sv.bounds.height + sv.contentInset.bottom let maxOffset = sv.contentSize.height - sv.bounds.height + sv.contentInset.bottom
guard maxOffset > 0 else { return } guard maxOffset > 0 else { return }
let delta = CGFloat(speed) * 60.0 * CGFloat(link.targetTimestamp - link.timestamp) let delta = CGFloat(speed) * 60.0 * CGFloat(link.targetTimestamp - link.timestamp)
let newY = min(sv.contentOffset.y + delta, maxOffset) let beforeY = sv.contentOffset.y
let newY = min(beforeY + delta, maxOffset)
sv.contentOffset.y = newY sv.contentOffset.y = newY
// Jitter detection
let wallDelta = lastTickTime > 0 ? (now - lastTickTime) * 1000 : 0
let csChanged = sv.contentSize.height != lastContentSize
tickCount += 1
if csChanged || wallDelta > 25 || tickCount % 180 == 0 {
print("[READER-DBG] \(csChanged || wallDelta > 25 ? "⚠️" : "") wallΔ=\(String(format:"%.1f",wallDelta))ms y=\(Int(newY)) csH=\(Int(sv.contentSize.height)) csΔ=\(Int(sv.contentSize.height - lastContentSize)) speed=\(String(format:"%.2f",speed)) dist=\(Int(maxOffset - newY))")
}
lastTickTime = now
lastContentSize = sv.contentSize.height
originalDelegate?.scrollViewDidScroll?(sv) originalDelegate?.scrollViewDidScroll?(sv)
// Trigger load more when within 500pt of bottom // Trigger load more when within 500pt of bottom