feat: Implement JavaScript-based scroll tracking in BookmarkDetailLegacyView
- Added scroll progress tracking via JavaScript in WebView - Implemented 3% threshold to reduce message frequency - Removed SwiftUI onScrollGeometryChange and onScrollPhaseChange - Cleaned up unused state variables (scrollViewHeight, currentScrollOffset) - Removed Combine import (no longer needed) - Disabled JumpButton scroll-to-position (requires JavaScript implementation) This approach offloads scroll tracking to the WebView's JavaScript, reducing SwiftUI state updates and improving performance.
This commit is contained in:
parent
171bf881fb
commit
32dbab400e
@ -1,6 +1,5 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SafariServices
|
import SafariServices
|
||||||
import Combine
|
|
||||||
|
|
||||||
struct BookmarkDetailLegacyView: View {
|
struct BookmarkDetailLegacyView: View {
|
||||||
let bookmarkId: String
|
let bookmarkId: String
|
||||||
@ -13,8 +12,6 @@ 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 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
|
||||||
@ -51,11 +48,19 @@ struct BookmarkDetailLegacyView: View {
|
|||||||
JumpButton()
|
JumpButton()
|
||||||
}
|
}
|
||||||
if let settings = viewModel.settings, !viewModel.articleContent.isEmpty {
|
if let settings = viewModel.settings, !viewModel.articleContent.isEmpty {
|
||||||
WebView(htmlContent: viewModel.articleContent, settings: settings, onHeightChange: { height in
|
WebView(
|
||||||
|
htmlContent: viewModel.articleContent,
|
||||||
|
settings: settings,
|
||||||
|
onHeightChange: { height in
|
||||||
if webViewHeight != height {
|
if webViewHeight != height {
|
||||||
webViewHeight = height
|
webViewHeight = height
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
onScroll: { scrollPercent in
|
||||||
|
readingProgress = scrollPercent
|
||||||
|
viewModel.debouncedUpdateReadProgress(id: bookmarkId, progress: scrollPercent, anchor: nil)
|
||||||
|
}
|
||||||
|
)
|
||||||
.frame(height: webViewHeight)
|
.frame(height: webViewHeight)
|
||||||
.cornerRadius(14)
|
.cornerRadius(14)
|
||||||
.padding(.horizontal, 4)
|
.padding(.horizontal, 4)
|
||||||
@ -94,33 +99,6 @@ struct BookmarkDetailLegacyView: View {
|
|||||||
}
|
}
|
||||||
.ignoresSafeArea(edges: .top)
|
.ignoresSafeArea(edges: .top)
|
||||||
.scrollPosition($scrollPosition)
|
.scrollPosition($scrollPosition)
|
||||||
.onScrollGeometryChange(for: CGFloat.self) { geo in
|
|
||||||
geo.contentOffset.y
|
|
||||||
} action: { oldValue, newValue in
|
|
||||||
// Just track current offset, don't calculate yet
|
|
||||||
currentScrollOffset = newValue
|
|
||||||
}
|
|
||||||
.onScrollGeometryChange(for: CGFloat.self) { geo in
|
|
||||||
geo.containerSize.height
|
|
||||||
} action: { oldValue, newValue in
|
|
||||||
scrollViewHeight = newValue
|
|
||||||
}
|
|
||||||
.onScrollPhaseChange { oldPhase, newPhase in
|
|
||||||
// Only calculate progress when scrolling ends
|
|
||||||
if oldPhase == .interacting && newPhase == .idle {
|
|
||||||
let offset = currentScrollOffset
|
|
||||||
let maxOffset = webViewHeight - geometry.size.height
|
|
||||||
let rawProgress = offset / (maxOffset > 0 ? maxOffset : 1)
|
|
||||||
let progress = min(max(rawProgress, 0), 1)
|
|
||||||
|
|
||||||
// Only update if change is significant (> 5%)
|
|
||||||
let threshold: Double = 0.05
|
|
||||||
if abs(progress - readingProgress) > threshold {
|
|
||||||
readingProgress = progress
|
|
||||||
viewModel.debouncedUpdateReadProgress(id: bookmarkId, progress: progress, anchor: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
@ -449,12 +427,10 @@ struct BookmarkDetailLegacyView: View {
|
|||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func JumpButton() -> some View {
|
func JumpButton() -> some View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
let maxOffset = webViewHeight - scrollViewHeight
|
// TODO: Implement scroll-to-position via JavaScript
|
||||||
let offset = maxOffset * (Double(viewModel.readProgress) / 100.0)
|
// Since we're now using JavaScript-based scroll tracking,
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
// we need to send a message to the WebView to scroll to the position
|
||||||
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)
|
||||||
|
|||||||
@ -264,21 +264,24 @@ struct WebView: UIViewRepresentable {
|
|||||||
document.querySelectorAll('img').forEach(img => {
|
document.querySelectorAll('img').forEach(img => {
|
||||||
img.addEventListener('load', debouncedHeightUpdate);
|
img.addEventListener('load', debouncedHeightUpdate);
|
||||||
});
|
});
|
||||||
|
let lastSent = { value: 0 };
|
||||||
window.addEventListener('scroll', function() {
|
window.addEventListener('scroll', function() {
|
||||||
isScrolling = true;
|
isScrolling = true;
|
||||||
|
|
||||||
|
let scrollTop = window.scrollY || document.documentElement.scrollTop;
|
||||||
|
let docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
||||||
|
let scrollPercent = docHeight > 0 ? (scrollTop / docHeight) * 100 : 0;
|
||||||
|
|
||||||
|
if (Math.abs(scrollPercent - lastSent.value) >= 3) {
|
||||||
|
window.webkit.messageHandlers.scrollProgress.postMessage(scrollPercent / 100);
|
||||||
|
lastSent.value = scrollPercent;
|
||||||
|
}
|
||||||
|
|
||||||
clearTimeout(scrollTimeout);
|
clearTimeout(scrollTimeout);
|
||||||
|
|
||||||
scrollTimeout = setTimeout(function() {
|
scrollTimeout = setTimeout(function() {
|
||||||
var scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
||||||
var docHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
|
|
||||||
var progress = docHeight > 0 ? scrollTop / docHeight : 0;
|
|
||||||
window.webkit.messageHandlers.scrollProgress.postMessage(progress);
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
isScrolling = false;
|
isScrolling = false;
|
||||||
debouncedHeightUpdate();
|
debouncedHeightUpdate();
|
||||||
}, 200);
|
}, 200);
|
||||||
}, 16);
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user