revert: Remove JavaScript scroll tracking, back to SwiftUI-based solution
- Removed JavaScript scroll event listeners and console.log debugging - Removed WebViewCoordinator.updateScrollProgress() method - Removed onExternalScrollUpdate callback - Removed webView reference and lastSentProgress from coordinator - Restored scrollViewHeight state variable - Restored JumpButton functionality with ScrollPosition - Back to onScrollPhaseChange with 3% threshold for reading progress The JavaScript approach didn't work because WebView scrolling is disabled (embedded in SwiftUI ScrollView). The SwiftUI-based solution is cleaner and performs well with onScrollPhaseChange.
This commit is contained in:
parent
5c9c00134a
commit
a782a27eea
@ -12,11 +12,11 @@ struct BookmarkDetailLegacyView: View {
|
|||||||
@State private var showingFontSettings = false
|
@State private var showingFontSettings = false
|
||||||
@State private var showingLabelsSheet = false
|
@State private var showingLabelsSheet = false
|
||||||
@State private var readingProgress: Double = 0.0
|
@State private var readingProgress: Double = 0.0
|
||||||
|
@State private var scrollViewHeight: CGFloat = 1
|
||||||
@State private var currentScrollOffset: CGFloat = 0
|
@State private var currentScrollOffset: CGFloat = 0
|
||||||
@State private var showJumpToProgressButton: Bool = false
|
@State private var showJumpToProgressButton: Bool = false
|
||||||
@State private var scrollPosition = ScrollPosition(edge: .top)
|
@State private var scrollPosition = ScrollPosition(edge: .top)
|
||||||
@State private var showingImageViewer = false
|
@State private var showingImageViewer = false
|
||||||
@State private var webViewCoordinator: WebViewCoordinator? = nil
|
|
||||||
|
|
||||||
// MARK: - Envs
|
// MARK: - Envs
|
||||||
|
|
||||||
@ -57,15 +57,6 @@ struct BookmarkDetailLegacyView: View {
|
|||||||
if webViewHeight != height {
|
if webViewHeight != height {
|
||||||
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)
|
.frame(height: webViewHeight)
|
||||||
@ -111,17 +102,25 @@ struct BookmarkDetailLegacyView: View {
|
|||||||
} action: { oldValue, newValue in
|
} action: { oldValue, newValue in
|
||||||
currentScrollOffset = newValue
|
currentScrollOffset = newValue
|
||||||
}
|
}
|
||||||
|
.onScrollGeometryChange(for: CGFloat.self) { geo in
|
||||||
|
geo.containerSize.height
|
||||||
|
} action: { oldValue, newValue in
|
||||||
|
scrollViewHeight = newValue
|
||||||
|
}
|
||||||
.onScrollPhaseChange { oldPhase, newPhase in
|
.onScrollPhaseChange { oldPhase, newPhase in
|
||||||
// Only calculate when scrolling ends (interacting -> idle)
|
// Only calculate when scrolling ends (interacting -> idle)
|
||||||
if oldPhase == .interacting && newPhase == .idle {
|
if oldPhase == .interacting && newPhase == .idle {
|
||||||
print("📊 Scroll ended at offset: \(currentScrollOffset)")
|
|
||||||
let offset = currentScrollOffset
|
let offset = currentScrollOffset
|
||||||
let maxOffset = webViewHeight - geometry.size.height
|
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)")
|
// Only update if change >= 3%
|
||||||
|
let threshold: Double = 0.03
|
||||||
// Send to WebView coordinator which handles the 3% threshold
|
if abs(progress - readingProgress) >= threshold {
|
||||||
webViewCoordinator?.updateScrollProgress(offset: offset, maxOffset: maxOffset)
|
readingProgress = progress
|
||||||
|
viewModel.debouncedUpdateReadProgress(id: bookmarkId, progress: progress, anchor: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -452,10 +451,12 @@ struct BookmarkDetailLegacyView: View {
|
|||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func JumpButton() -> some View {
|
func JumpButton() -> some View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
// TODO: Implement scroll-to-position via JavaScript
|
let maxOffset = webViewHeight - scrollViewHeight
|
||||||
// Since we're now using JavaScript-based scroll tracking,
|
let offset = maxOffset * (Double(viewModel.readProgress) / 100.0)
|
||||||
// we need to send a message to the WebView to scroll to the position
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||||
|
scrollPosition = ScrollPosition(y: offset)
|
||||||
showJumpToProgressButton = false
|
showJumpToProgressButton = false
|
||||||
|
}
|
||||||
}) {
|
}) {
|
||||||
Text("Jump to last read position (\(viewModel.readProgress)%)")
|
Text("Jump to last read position (\(viewModel.readProgress)%)")
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
|
|||||||
@ -6,7 +6,6 @@ struct WebView: UIViewRepresentable {
|
|||||||
let settings: Settings
|
let settings: Settings
|
||||||
let onHeightChange: (CGFloat) -> Void
|
let onHeightChange: (CGFloat) -> Void
|
||||||
var onScroll: ((Double) -> Void)? = nil
|
var onScroll: ((Double) -> Void)? = nil
|
||||||
var onExternalScrollUpdate: ((WebViewCoordinator) -> Void)? = nil
|
|
||||||
@Environment(\.colorScheme) private var colorScheme
|
@Environment(\.colorScheme) private var colorScheme
|
||||||
|
|
||||||
func makeUIView(context: Context) -> WKWebView {
|
func makeUIView(context: Context) -> WKWebView {
|
||||||
@ -23,8 +22,6 @@ struct WebView: UIViewRepresentable {
|
|||||||
webView.isOpaque = false
|
webView.isOpaque = false
|
||||||
webView.backgroundColor = UIColor.clear
|
webView.backgroundColor = UIColor.clear
|
||||||
|
|
||||||
print("🟢 WebView created with scrolling DISABLED (embedded in ScrollView)")
|
|
||||||
|
|
||||||
// Allow text selection and copying
|
// Allow text selection and copying
|
||||||
webView.allowsBackForwardNavigationGestures = false
|
webView.allowsBackForwardNavigationGestures = false
|
||||||
webView.allowsLinkPreview = true
|
webView.allowsLinkPreview = true
|
||||||
@ -33,10 +30,6 @@ struct WebView: UIViewRepresentable {
|
|||||||
webView.configuration.userContentController.add(context.coordinator, name: "scrollProgress")
|
webView.configuration.userContentController.add(context.coordinator, name: "scrollProgress")
|
||||||
context.coordinator.onHeightChange = onHeightChange
|
context.coordinator.onHeightChange = onHeightChange
|
||||||
context.coordinator.onScroll = onScroll
|
context.coordinator.onScroll = onScroll
|
||||||
context.coordinator.webView = webView
|
|
||||||
|
|
||||||
// Notify parent that coordinator is ready
|
|
||||||
onExternalScrollUpdate?(context.coordinator)
|
|
||||||
|
|
||||||
return webView
|
return webView
|
||||||
}
|
}
|
||||||
@ -271,38 +264,6 @@ struct WebView: UIViewRepresentable {
|
|||||||
document.querySelectorAll('img').forEach(img => {
|
document.querySelectorAll('img').forEach(img => {
|
||||||
img.addEventListener('load', debouncedHeightUpdate);
|
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');
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -351,9 +312,6 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler
|
|||||||
var onHeightChange: ((CGFloat) -> Void)?
|
var onHeightChange: ((CGFloat) -> Void)?
|
||||||
var onScroll: ((Double) -> Void)?
|
var onScroll: ((Double) -> Void)?
|
||||||
|
|
||||||
// WebView reference
|
|
||||||
weak var webView: WKWebView?
|
|
||||||
|
|
||||||
// Height management
|
// Height management
|
||||||
var lastHeight: CGFloat = 0
|
var lastHeight: CGFloat = 0
|
||||||
var pendingHeight: CGFloat = 0
|
var pendingHeight: CGFloat = 0
|
||||||
@ -364,7 +322,6 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler
|
|||||||
var scrollVelocity: Double = 0
|
var scrollVelocity: Double = 0
|
||||||
var lastScrollTime: Date = Date()
|
var lastScrollTime: Date = Date()
|
||||||
var scrollEndTimer: Timer?
|
var scrollEndTimer: Timer?
|
||||||
var lastSentProgress: Double = 0
|
|
||||||
|
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
private var isCleanedUp = false
|
private var isCleanedUp = false
|
||||||
@ -385,16 +342,12 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||||
print("🔔 Swift received message: \(message.name)")
|
|
||||||
|
|
||||||
if message.name == "heightUpdate", let height = message.body as? CGFloat {
|
if message.name == "heightUpdate", let height = message.body as? CGFloat {
|
||||||
print("📏 Height update: \(height)px")
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.handleHeightUpdate(height: height)
|
self.handleHeightUpdate(height: height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if message.name == "scrollProgress", let progress = message.body as? Double {
|
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 {
|
DispatchQueue.main.async {
|
||||||
self.handleScrollProgress(progress: progress)
|
self.handleScrollProgress(progress: progress)
|
||||||
}
|
}
|
||||||
@ -415,8 +368,6 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func handleScrollProgress(progress: Double) {
|
private func handleScrollProgress(progress: Double) {
|
||||||
print("🎯 handleScrollProgress called with: \(String(format: "%.3f", progress))")
|
|
||||||
|
|
||||||
let now = Date()
|
let now = Date()
|
||||||
let timeDelta = now.timeIntervalSince(lastScrollTime)
|
let timeDelta = now.timeIntervalSince(lastScrollTime)
|
||||||
|
|
||||||
@ -436,7 +387,6 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler
|
|||||||
self?.handleScrollEnd()
|
self?.handleScrollEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
print("🚀 Calling onScroll callback with progress: \(String(format: "%.3f", progress))")
|
|
||||||
onScroll?(progress)
|
onScroll?(progress)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,23 +416,6 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler
|
|||||||
onHeightChange?(height)
|
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() {
|
func cleanup() {
|
||||||
guard !isCleanedUp else { return }
|
guard !isCleanedUp else { return }
|
||||||
isCleanedUp = true
|
isCleanedUp = true
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user