update README with new iPhone and iPad screenshots
This commit is contained in:
parent
3abeb3f3e4
commit
c8c93b76da
23
README.md
23
README.md
@ -9,13 +9,24 @@ https://codeberg.org/readeck/readeck
|
||||
|
||||
## Screenshots
|
||||
|
||||
### iPhone
|
||||
|
||||
<p align="center">
|
||||
<img src="screenshots/main.webp" height="400" alt="Main View">
|
||||
<img src="screenshots/detail.webp" height="400" alt="Detail View">
|
||||
<img src="screenshots/new.webp" height="400" alt="Add Bookmark">
|
||||
<img src="screenshots/more.webp" height="400" alt="More Options">
|
||||
<img src="screenshots/share.webp" height="400" alt="Share Extension">
|
||||
<img src="screenshots/ipad.webp" height="400" alt="iPad View">
|
||||
<img src="screenshots/iphone_1.png" height="400" alt="iPhone Screenshot 1">
|
||||
<img src="screenshots/iphone_2.png" height="400" alt="iPhone Screenshot 2">
|
||||
<img src="screenshots/iphone_3.png" height="400" alt="iPhone Screenshot 3">
|
||||
<img src="screenshots/iphone_4.png" height="400" alt="iPhone Screenshot 4">
|
||||
<img src="screenshots/iphone_5.png" height="400" alt="iPhone Screenshot 5">
|
||||
</p>
|
||||
|
||||
### iPad
|
||||
|
||||
<p align="center">
|
||||
<img src="screenshots/ipad_1.jpg" height="400" alt="iPad Screenshot 1">
|
||||
<img src="screenshots/ipad_2.jpg" height="400" alt="iPad Screenshot 2">
|
||||
<img src="screenshots/ipad_3.jpg" height="400" alt="iPad Screenshot 3">
|
||||
<img src="screenshots/ipad_4.jpg" height="400" alt="iPad Screenshot 4">
|
||||
<img src="screenshots/ipad_5.jpg" height="400" alt="iPad Screenshot 5">
|
||||
</p>
|
||||
|
||||
## Download
|
||||
|
||||
@ -83,6 +83,7 @@
|
||||
Data/CoreData/CoreDataManager.swift,
|
||||
"Data/Extensions/NSManagedObjectContext+SafeFetch.swift",
|
||||
Data/KeychainHelper.swift,
|
||||
Data/Utils/LabelUtils.swift,
|
||||
Domain/Model/Bookmark.swift,
|
||||
Domain/Model/BookmarkLabel.swift,
|
||||
Logger.swift,
|
||||
|
||||
@ -65,7 +65,6 @@ struct BookmarkDetailView: View {
|
||||
.frame(height: webViewHeight)
|
||||
.cornerRadius(14)
|
||||
.padding(.horizontal, 4)
|
||||
.animation(.easeInOut, value: webViewHeight)
|
||||
} else if viewModel.isLoadingArticle {
|
||||
ProgressView("Loading article...")
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
|
||||
@ -228,9 +228,24 @@ struct WebView: UIViewRepresentable {
|
||||
<body>
|
||||
\(htmlContent)
|
||||
<script>
|
||||
let lastHeight = 0;
|
||||
let heightUpdateTimeout = null;
|
||||
let scrollTimeout = null;
|
||||
let isScrolling = false;
|
||||
|
||||
function updateHeight() {
|
||||
const height = document.body.scrollHeight;
|
||||
window.webkit.messageHandlers.heightUpdate.postMessage(height);
|
||||
|
||||
// Only send height update if it changed significantly and we're not scrolling
|
||||
if (Math.abs(height - lastHeight) > 5 && !isScrolling) {
|
||||
lastHeight = height;
|
||||
window.webkit.messageHandlers.heightUpdate.postMessage(height);
|
||||
}
|
||||
}
|
||||
|
||||
function debouncedHeightUpdate() {
|
||||
clearTimeout(heightUpdateTimeout);
|
||||
heightUpdateTimeout = setTimeout(updateHeight, 100);
|
||||
}
|
||||
|
||||
window.addEventListener('load', updateHeight);
|
||||
@ -238,14 +253,29 @@ struct WebView: UIViewRepresentable {
|
||||
|
||||
// Höhe bei Bild-Ladevorgängen aktualisieren
|
||||
document.querySelectorAll('img').forEach(img => {
|
||||
img.addEventListener('load', updateHeight);
|
||||
img.addEventListener('load', debouncedHeightUpdate);
|
||||
});
|
||||
// Scroll progress reporting
|
||||
|
||||
// Throttled scroll progress reporting
|
||||
window.addEventListener('scroll', 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);
|
||||
isScrolling = true;
|
||||
|
||||
// Clear existing timeout
|
||||
clearTimeout(scrollTimeout);
|
||||
|
||||
// Throttle scroll events to reduce frequency
|
||||
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);
|
||||
|
||||
// Reset scrolling state after a delay
|
||||
setTimeout(function() {
|
||||
isScrolling = false;
|
||||
debouncedHeightUpdate(); // Check for height changes after scrolling stops
|
||||
}, 200);
|
||||
}, 16); // ~60fps throttling
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
@ -284,9 +314,13 @@ struct WebView: UIViewRepresentable {
|
||||
class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
|
||||
var onHeightChange: ((CGFloat) -> Void)?
|
||||
var onScroll: ((Double) -> Void)?
|
||||
var hasHeightUpdate: Bool = false
|
||||
var isScrolling: Bool = false
|
||||
var scrollEndTimer: Timer?
|
||||
var heightUpdateTimer: Timer?
|
||||
var lastHeight: CGFloat = 0
|
||||
var pendingHeight: CGFloat = 0
|
||||
var scrollVelocity: Double = 0
|
||||
var lastScrollTime: Date = Date()
|
||||
|
||||
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
||||
if navigationAction.navigationType == .linkActivated {
|
||||
@ -302,26 +336,75 @@ class WebViewCoordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler
|
||||
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||
if message.name == "heightUpdate", let height = message.body as? CGFloat {
|
||||
DispatchQueue.main.async {
|
||||
// Block height updates during active scrolling to prevent flicker
|
||||
if !self.isScrolling && !self.hasHeightUpdate {
|
||||
self.onHeightChange?(height)
|
||||
self.hasHeightUpdate = true
|
||||
}
|
||||
self.handleHeightUpdate(height: height)
|
||||
}
|
||||
}
|
||||
if message.name == "scrollProgress", let progress = message.body as? Double {
|
||||
DispatchQueue.main.async {
|
||||
// Track scrolling state
|
||||
self.isScrolling = true
|
||||
|
||||
// Reset scrolling state after scroll ends
|
||||
self.scrollEndTimer?.invalidate()
|
||||
self.scrollEndTimer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { _ in
|
||||
self.isScrolling = false
|
||||
}
|
||||
|
||||
self.onScroll?(progress)
|
||||
self.handleScrollProgress(progress: progress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleHeightUpdate(height: CGFloat) {
|
||||
// Store the pending height
|
||||
pendingHeight = height
|
||||
|
||||
// If we're actively scrolling, defer the height update
|
||||
if isScrolling {
|
||||
return
|
||||
}
|
||||
|
||||
// Apply height update immediately if not scrolling
|
||||
applyHeightUpdate(height: height)
|
||||
}
|
||||
|
||||
private func handleScrollProgress(progress: Double) {
|
||||
let now = Date()
|
||||
let timeDelta = now.timeIntervalSince(lastScrollTime)
|
||||
|
||||
// Calculate scroll velocity to detect fast scrolling
|
||||
if timeDelta > 0 {
|
||||
scrollVelocity = abs(progress) / timeDelta
|
||||
}
|
||||
|
||||
lastScrollTime = now
|
||||
isScrolling = true
|
||||
|
||||
// Longer delay for scroll end detection, especially during fast scrolling
|
||||
let scrollEndDelay: TimeInterval = scrollVelocity > 2.0 ? 0.8 : 0.5
|
||||
|
||||
scrollEndTimer?.invalidate()
|
||||
scrollEndTimer = Timer.scheduledTimer(withTimeInterval: scrollEndDelay, repeats: false) { [weak self] _ in
|
||||
self?.handleScrollEnd()
|
||||
}
|
||||
|
||||
onScroll?(progress)
|
||||
}
|
||||
|
||||
private func handleScrollEnd() {
|
||||
isScrolling = false
|
||||
scrollVelocity = 0
|
||||
|
||||
// Apply any pending height update after scrolling ends
|
||||
if pendingHeight != lastHeight && pendingHeight > 0 {
|
||||
// Add small delay to ensure scroll has fully stopped
|
||||
heightUpdateTimer?.invalidate()
|
||||
heightUpdateTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.applyHeightUpdate(height: self.pendingHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func applyHeightUpdate(height: CGFloat) {
|
||||
// Only update if height actually changed significantly
|
||||
let heightDifference = abs(height - lastHeight)
|
||||
if heightDifference < 5 { // Ignore tiny height changes that cause flicker
|
||||
return
|
||||
}
|
||||
|
||||
lastHeight = height
|
||||
onHeightChange?(height)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user