diff --git a/readeck/UI/BookmarkDetail/BookmarkDetailLegacyView.swift b/readeck/UI/BookmarkDetail/BookmarkDetailLegacyView.swift index cc33e34..d135b96 100644 --- a/readeck/UI/BookmarkDetail/BookmarkDetailLegacyView.swift +++ b/readeck/UI/BookmarkDetail/BookmarkDetailLegacyView.swift @@ -12,11 +12,11 @@ struct BookmarkDetailLegacyView: View { @State private var showingFontSettings = false @State private var showingLabelsSheet = false @State private var readingProgress: Double = 0.0 + @State private var scrollViewHeight: CGFloat = 1 @State private var currentScrollOffset: CGFloat = 0 @State private var showJumpToProgressButton: Bool = false @State private var scrollPosition = ScrollPosition(edge: .top) @State private var showingImageViewer = false - @State private var webViewCoordinator: WebViewCoordinator? = nil // MARK: - Envs @@ -57,15 +57,6 @@ struct BookmarkDetailLegacyView: View { if webViewHeight != height { webViewHeight = height } - }, - onScroll: { scrollPercent in - print("πŸ”₯ BookmarkDetailLegacyView onScroll callback: \(String(format: "%.3f", scrollPercent)) (\(String(format: "%.1f", scrollPercent * 100))%)") - readingProgress = scrollPercent - viewModel.debouncedUpdateReadProgress(id: bookmarkId, progress: scrollPercent, anchor: nil) - }, - onExternalScrollUpdate: { coordinator in - print("🎯 WebView coordinator ready, storing reference") - webViewCoordinator = coordinator } ) .frame(height: webViewHeight) @@ -111,17 +102,25 @@ struct BookmarkDetailLegacyView: View { } action: { oldValue, newValue in currentScrollOffset = newValue } + .onScrollGeometryChange(for: CGFloat.self) { geo in + geo.containerSize.height + } action: { oldValue, newValue in + scrollViewHeight = newValue + } .onScrollPhaseChange { oldPhase, newPhase in // Only calculate when scrolling ends (interacting -> idle) if oldPhase == .interacting && newPhase == .idle { - print("πŸ“Š Scroll ended at offset: \(currentScrollOffset)") let offset = currentScrollOffset let maxOffset = webViewHeight - geometry.size.height + let rawProgress = offset / (maxOffset > 0 ? maxOffset : 1) + let progress = min(max(rawProgress, 0), 1) - print("🎯 Sending to WebView coordinator: offset=\(offset), maxOffset=\(maxOffset)") - - // Send to WebView coordinator which handles the 3% threshold - webViewCoordinator?.updateScrollProgress(offset: offset, maxOffset: maxOffset) + // Only update if change >= 3% + let threshold: Double = 0.03 + if abs(progress - readingProgress) >= threshold { + readingProgress = progress + viewModel.debouncedUpdateReadProgress(id: bookmarkId, progress: progress, anchor: nil) + } } } } @@ -452,10 +451,12 @@ struct BookmarkDetailLegacyView: View { @ViewBuilder func JumpButton() -> some View { Button(action: { - // TODO: Implement scroll-to-position via JavaScript - // Since we're now using JavaScript-based scroll tracking, - // we need to send a message to the WebView to scroll to the position - showJumpToProgressButton = false + let maxOffset = webViewHeight - scrollViewHeight + let offset = maxOffset * (Double(viewModel.readProgress) / 100.0) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + scrollPosition = ScrollPosition(y: offset) + showJumpToProgressButton = false + } }) { Text("Jump to last read position (\(viewModel.readProgress)%)") .font(.subheadline) diff --git a/readeck/UI/Components/WebView.swift b/readeck/UI/Components/WebView.swift index 74d8763..e59c281 100644 --- a/readeck/UI/Components/WebView.swift +++ b/readeck/UI/Components/WebView.swift @@ -6,7 +6,6 @@ struct WebView: UIViewRepresentable { let settings: Settings let onHeightChange: (CGFloat) -> Void var onScroll: ((Double) -> Void)? = nil - var onExternalScrollUpdate: ((WebViewCoordinator) -> Void)? = nil @Environment(\.colorScheme) private var colorScheme func makeUIView(context: Context) -> WKWebView { @@ -23,20 +22,14 @@ struct WebView: UIViewRepresentable { webView.isOpaque = false webView.backgroundColor = UIColor.clear - print("🟒 WebView created with scrolling DISABLED (embedded in ScrollView)") - // Allow text selection and copying webView.allowsBackForwardNavigationGestures = false webView.allowsLinkPreview = true - + webView.configuration.userContentController.add(context.coordinator, name: "heightUpdate") webView.configuration.userContentController.add(context.coordinator, name: "scrollProgress") context.coordinator.onHeightChange = onHeightChange context.coordinator.onScroll = onScroll - context.coordinator.webView = webView - - // Notify parent that coordinator is ready - onExternalScrollUpdate?(context.coordinator) return webView } @@ -271,38 +264,6 @@ struct WebView: UIViewRepresentable { document.querySelectorAll('img').forEach(img => { img.addEventListener('load', debouncedHeightUpdate); }); - console.log('🟒 WebView JavaScript loaded'); - console.log('πŸ”΅ Document scroll enabled:', document.body.style.overflow); - console.log('πŸ”΅ Window innerHeight:', window.innerHeight); - console.log('πŸ”΅ Document scrollHeight:', document.documentElement.scrollHeight); - - let lastSent = { value: 0 }; - window.addEventListener('scroll', function() { - console.log('πŸ“œ Scroll event fired!'); - isScrolling = true; - - let scrollTop = window.scrollY || document.documentElement.scrollTop; - let docHeight = document.documentElement.scrollHeight - window.innerHeight; - let scrollPercent = docHeight > 0 ? (scrollTop / docHeight) * 100 : 0; - - console.log('πŸ“Š scrollTop:', scrollTop, 'docHeight:', docHeight, 'scrollPercent:', scrollPercent.toFixed(2) + '%'); - - if (Math.abs(scrollPercent - lastSent.value) >= 3) { - console.log('βœ… Sending scroll progress:', (scrollPercent / 100).toFixed(3)); - window.webkit.messageHandlers.scrollProgress.postMessage(scrollPercent / 100); - lastSent.value = scrollPercent; - } else { - console.log('⏸️ Skipping (change < 3%): ', Math.abs(scrollPercent - lastSent.value).toFixed(2) + '%'); - } - - clearTimeout(scrollTimeout); - scrollTimeout = setTimeout(function() { - isScrolling = false; - debouncedHeightUpdate(); - }, 200); - }, { passive: true }); - - console.log('🎯 Scroll listener attached'); @@ -351,9 +312,6 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler var onHeightChange: ((CGFloat) -> Void)? var onScroll: ((Double) -> Void)? - // WebView reference - weak var webView: WKWebView? - // Height management var lastHeight: CGFloat = 0 var pendingHeight: CGFloat = 0 @@ -364,7 +322,6 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler var scrollVelocity: Double = 0 var lastScrollTime: Date = Date() var scrollEndTimer: Timer? - var lastSentProgress: Double = 0 // Lifecycle private var isCleanedUp = false @@ -385,16 +342,12 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler } func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { - print("πŸ”” Swift received message: \(message.name)") - if message.name == "heightUpdate", let height = message.body as? CGFloat { - print("πŸ“ Height update: \(height)px") DispatchQueue.main.async { self.handleHeightUpdate(height: height) } } if message.name == "scrollProgress", let progress = message.body as? Double { - print("πŸ“Š Swift received scroll progress: \(String(format: "%.3f", progress)) (\(String(format: "%.1f", progress * 100))%)") DispatchQueue.main.async { self.handleScrollProgress(progress: progress) } @@ -415,8 +368,6 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler } private func handleScrollProgress(progress: Double) { - print("🎯 handleScrollProgress called with: \(String(format: "%.3f", progress))") - let now = Date() let timeDelta = now.timeIntervalSince(lastScrollTime) @@ -436,7 +387,6 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler self?.handleScrollEnd() } - print("πŸš€ Calling onScroll callback with progress: \(String(format: "%.3f", progress))") onScroll?(progress) } @@ -466,23 +416,6 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler onHeightChange?(height) } - // Method to receive scroll updates from SwiftUI ScrollView - func updateScrollProgress(offset: CGFloat, maxOffset: CGFloat) { - let progress = maxOffset > 0 ? min(max(offset / maxOffset, 0), 1) : 0 - - print("πŸ“Š External scroll update: offset=\(offset), maxOffset=\(maxOffset), progress=\(String(format: "%.3f", progress))") - - // Only send if change >= 3% - let threshold: Double = 0.03 - if abs(progress - lastSentProgress) >= threshold { - print("βœ… Calling onScroll callback with: \(String(format: "%.3f", progress))") - lastSentProgress = progress - onScroll?(progress) - } else { - print("⏸️ Skipping (change < 3%): \(String(format: "%.3f", abs(progress - lastSentProgress)))") - } - } - func cleanup() { guard !isCleanedUp else { return } isCleanedUp = true