fix: Use total content height for read progress calculation

Added ContentHeightPreferenceKey to track the total ScrollView content height.

The bug: Progress was calculated using only webViewHeight - containerHeight,
which ignores the header, title, and other content above the webview.

The fix: Use total content height (header + title + webview + archive section)
instead of just webViewHeight for accurate progress calculation.

Changes:
- Added ContentHeightPreferenceKey preference key
- Added contentHeight state variable
- Added background GeometryReader to VStack to measure total content height
- Changed progress calculation: contentHeight - containerHeight (not webViewHeight)

Applied to both BookmarkDetailLegacyView and BookmarkDetailView2.
This commit is contained in:
Ilyas Hallak 2025-10-10 20:27:17 +02:00
parent 4595a9b69f
commit bef6a9dc2f
2 changed files with 34 additions and 2 deletions

View File

@ -9,6 +9,14 @@ struct ScrollOffsetPreferenceKey: PreferenceKey {
} }
} }
// PreferenceKey for content height tracking
struct ContentHeightPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
struct BookmarkDetailLegacyView: View { struct BookmarkDetailLegacyView: View {
let bookmarkId: String let bookmarkId: String
@Binding var useNativeWebView: Bool @Binding var useNativeWebView: Bool
@ -17,6 +25,7 @@ struct BookmarkDetailLegacyView: View {
@State private var viewModel: BookmarkDetailViewModel @State private var viewModel: BookmarkDetailViewModel
@State private var webViewHeight: CGFloat = 300 @State private var webViewHeight: CGFloat = 300
@State private var contentHeight: CGFloat = 0
@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
@ -113,16 +122,27 @@ struct BookmarkDetailLegacyView: View {
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
} }
} }
.background(
GeometryReader { contentGeo in
Color.clear.preference(
key: ContentHeightPreferenceKey.self,
value: contentGeo.size.height
)
}
)
} }
.coordinateSpace(name: "scrollView") .coordinateSpace(name: "scrollView")
.clipped() .clipped()
.ignoresSafeArea(edges: .top) .ignoresSafeArea(edges: .top)
.scrollPosition($scrollPosition) .scrollPosition($scrollPosition)
.onPreferenceChange(ContentHeightPreferenceKey.self) { height in
contentHeight = height
}
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { offset in .onPreferenceChange(ScrollOffsetPreferenceKey.self) { offset in
// Calculate progress from scroll offset // Calculate progress from scroll offset
let scrollOffset = -offset.y // Negative because scroll goes down let scrollOffset = -offset.y // Negative because scroll goes down
let containerHeight = geometry.size.height let containerHeight = geometry.size.height
let maxOffset = webViewHeight - containerHeight let maxOffset = contentHeight - containerHeight
guard maxOffset > 0 else { return } guard maxOffset > 0 else { return }

View File

@ -10,6 +10,7 @@ struct BookmarkDetailView2: View {
@State private var viewModel: BookmarkDetailViewModel @State private var viewModel: BookmarkDetailViewModel
@State private var webViewHeight: CGFloat = 300 @State private var webViewHeight: CGFloat = 300
@State private var contentHeight: CGFloat = 0
@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
@ -120,16 +121,27 @@ struct BookmarkDetailView2: View {
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
} }
} }
.background(
GeometryReader { contentGeo in
Color.clear.preference(
key: ContentHeightPreferenceKey.self,
value: contentGeo.size.height
)
}
)
} }
.coordinateSpace(name: "scrollView") .coordinateSpace(name: "scrollView")
.clipped() .clipped()
.ignoresSafeArea(edges: .top) .ignoresSafeArea(edges: .top)
.scrollPosition($scrollPosition) .scrollPosition($scrollPosition)
.onPreferenceChange(ContentHeightPreferenceKey.self) { height in
contentHeight = height
}
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { offset in .onPreferenceChange(ScrollOffsetPreferenceKey.self) { offset in
// Calculate progress from scroll offset // Calculate progress from scroll offset
let scrollOffset = -offset.y let scrollOffset = -offset.y
let containerHeight = geometry.size.height let containerHeight = geometry.size.height
let maxOffset = webViewHeight - containerHeight let maxOffset = contentHeight - containerHeight
guard maxOffset > 0 else { return } guard maxOffset > 0 else { return }