diff --git a/ios/Platform/Platform/Features/Reader/ViewModels/ReaderViewModel.swift b/ios/Platform/Platform/Features/Reader/ViewModels/ReaderViewModel.swift index d496655..ad21dfd 100644 --- a/ios/Platform/Platform/Features/Reader/ViewModels/ReaderViewModel.swift +++ b/ios/Platform/Platform/Features/Reader/ViewModels/ReaderViewModel.swift @@ -94,15 +94,19 @@ final class ReaderViewModel { func loadMore() async { guard !isLoadingMore, hasMore else { return } isLoadingMore = true + print("[READER-DBG] ๐Ÿ“ฅ loadMore START offset=\(offset)") do { let list = try await fetchEntries(offset: offset) + let count = list.entries.count entries.append(contentsOf: list.entries) total = list.total - offset += list.entries.count + offset += count hasMore = offset < list.total + print("[READER-DBG] ๐Ÿ“ฅ loadMore END +\(count) total=\(entries.count) hasMore=\(hasMore)") } catch { self.error = error.localizedDescription + print("[READER-DBG] ๐Ÿ“ฅ loadMore ERROR: \(error)") } isLoadingMore = false } diff --git a/ios/Platform/Platform/Features/Reader/Views/EntryListView.swift b/ios/Platform/Platform/Features/Reader/Views/EntryListView.swift index cba81bf..7a9c180 100644 --- a/ios/Platform/Platform/Features/Reader/Views/EntryListView.swift +++ b/ios/Platform/Platform/Features/Reader/Views/EntryListView.swift @@ -123,6 +123,7 @@ struct EntryListView: View { frame.maxY < 30 else { return } markedByScroll.insert(entryId) + print("[READER-DBG] ๐Ÿ“– markRead id=\(entryId) autoScroll=\(isAutoScrolling) maxY=\(Int(frame.maxY))") if isAutoScrolling { // Defer visual update โ€” contentSize changes cause jitter diff --git a/ios/Platform/Platform/Features/Reader/Views/ScrollViewDriver.swift b/ios/Platform/Platform/Features/Reader/Views/ScrollViewDriver.swift index 88cf223..9507683 100644 --- a/ios/Platform/Platform/Features/Reader/Views/ScrollViewDriver.swift +++ b/ios/Platform/Platform/Features/Reader/Views/ScrollViewDriver.swift @@ -98,20 +98,36 @@ struct ScrollViewDriver: UIViewRepresentable { displayLink = nil } + private var lastTickTime: CFAbsoluteTime = 0 + private var lastContentSize: CGFloat = 0 + private var tickCount = 0 + @objc private func tick(_ link: CADisplayLink) { guard let sv = scrollView else { stopAndNotify() return } + let now = CFAbsoluteTimeGetCurrent() let maxOffset = sv.contentSize.height - sv.bounds.height + sv.contentInset.bottom guard maxOffset > 0 else { return } 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 + // 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) // Trigger load more when within 500pt of bottom