diff --git a/App/Entity/Entry.swift b/App/Entity/Entry.swift
index ff7756e..116b09a 100644
--- a/App/Entity/Entry.swift
+++ b/App/Entity/Entry.swift
@@ -1,6 +1,7 @@
import CoreData
import CoreSpotlight
import Foundation
+import HTMLEntities
import SharedLib
// import MobileCoreServices
@@ -80,4 +81,9 @@ extension Entry {
}
return Double(screenPosition)
}
+
+ var titleHtml: String {
+ let escapedTitle = (title ?? "").htmlEscape()
+ return "
\(escapedTitle)
"
+ }
}
diff --git a/App/Features/Entry/EntriesView.swift b/App/Features/Entry/EntriesView.swift
index 4893d56..25794fd 100644
--- a/App/Features/Entry/EntriesView.swift
+++ b/App/Features/Entry/EntriesView.swift
@@ -14,17 +14,23 @@ struct EntriesView: View {
@State private var showPaywallWallabagPlus = false
var body: some View {
- VStack {
- #if os(iOS)
- PasteBoardView()
- #endif
- SearchView(searchViewModel: searchViewModel)
- EntriesListView(
- predicate: searchViewModel.predicate,
- entriesSortedById: entriesSortedById,
- entriesSortedByReadingTime: entriesSortedByReadingTime,
- entriesSortedByAscending: entriesSortedByAscending
- )
+ EntriesListView(
+ predicate: searchViewModel.predicate,
+ entriesSortedById: entriesSortedById,
+ entriesSortedByReadingTime: entriesSortedByReadingTime,
+ entriesSortedByAscending: entriesSortedByAscending
+ )
+ .scrollContentBackground(.hidden)
+ .safeAreaInset(edge: .top) {
+ VStack(spacing: 0) {
+ #if os(iOS)
+ PasteBoardView()
+ #endif
+ SearchView(searchViewModel: searchViewModel)
+ .padding(.bottom, 8)
+ Divider()
+ }
+ .background(.ultraThinMaterial)
}
.onChange(of: entriesSortedById) { _, newValue in
if newValue {
@@ -94,6 +100,8 @@ struct EntriesView: View {
PaywallView(displayCloseButton: true)
}
.navigationTitle("Entries")
+ .navigationBarTitleDisplayMode(.inline)
+ .ignoresSafeArea(.all, edges: .bottom)
}
}
diff --git a/App/Features/Entry/EntryView.swift b/App/Features/Entry/EntryView.swift
index b6eeb41..9562223 100644
--- a/App/Features/Entry/EntryView.swift
+++ b/App/Features/Entry/EntryView.swift
@@ -23,18 +23,14 @@ struct EntryView: View {
#endif
var body: some View {
- VStack(alignment: .leading) {
- Text(entry.title?.htmlUnescape() ?? "Entry")
- .font(.title)
- .fontWeight(.black)
- .lineLimit(2)
- .padding(.horizontal)
- ProgressView(value: min(progress, 1), total: 1)
- WebView(entry: entry, progress: $progress)
- }
- .addSwipeToBack {
- dismiss()
- }
+ WebView(entry: entry, progress: $progress)
+ .ignoresSafeArea()
+ .safeAreaInset(edge: .top) {
+ ProgressView(value: max(0, min(progress, 1)), total: 1)
+ }
+ .addSwipeToBack {
+ dismiss()
+ }
.toolbar {
ToolbarItem(placement: toolbarPlacement) {
Menu(content: {
@@ -79,6 +75,9 @@ struct EntryView: View {
TagListFor(entry: entry)
.presentationDetents([.medium, .large])
}
+ .toolbarBackground(.ultraThinMaterial, for: .bottomBar)
+ .toolbarBackground(.visible, for: .bottomBar)
+ .ignoresSafeArea(.all, edges: .bottom)
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
diff --git a/App/Features/Entry/WebView.swift b/App/Features/Entry/WebView.swift
index 72ac739..6c776d3 100644
--- a/App/Features/Entry/WebView.swift
+++ b/App/Features/Entry/WebView.swift
@@ -6,7 +6,6 @@ import WebKit
#if os(iOS)
struct WebView: UIViewRepresentable {
var entry: Entry
- private(set) var wkWebView = WKWebView(frame: .zero)
@EnvironmentObject var appSetting: AppSetting
@Binding var progress: Double
@@ -26,19 +25,27 @@ import WebKit
super.init()
}
- func webViewToLastPosition() {
- webView.wkWebView.scrollView.setContentOffset(
- CGPoint(
- x: 0.0,
- y: webView.entry.screenPositionForWebView
- ),
- animated: true
- )
+ func webViewToLastPosition(in webView: WKWebView) {
+ let position = self.webView.entry.screenPositionForWebView
+ if position > 0 {
+ // Check if content is actually loaded by verifying contentSize
+ if webView.scrollView.contentSize.height > webView.bounds.height {
+ webView.scrollView.setContentOffset(
+ CGPoint(x: 0.0, y: position),
+ animated: true
+ )
+ } else {
+ // Content not ready yet, try again after a minimal delay
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
+ self.webViewToLastPosition(in: webView)
+ }
+ }
+ }
}
func webView(_ webView: WKWebView, didFinish _: WKNavigation!) {
- webViewToLastPosition()
webView.fontSizePercent(appSetting.webFontSizePercent)
+ self.webViewToLastPosition(in: webView)
}
func webView(_: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
@@ -67,7 +74,12 @@ import WebKit
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
- webView.progress = scrollView.contentOffset.y / (scrollView.contentSize.height - scrollView.bounds.height)
+ let scrollableHeight = scrollView.contentSize.height - scrollView.bounds.height
+ if scrollableHeight > 0 {
+ webView.progress = scrollView.contentOffset.y / scrollableHeight
+ } else {
+ webView.progress = 0
+ }
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
@@ -79,11 +91,17 @@ import WebKit
}
func makeUIView(context: Context) -> WKWebView {
- wkWebView.navigationDelegate = context.coordinator
- wkWebView.scrollView.delegate = context.coordinator
- wkWebView.load(content: entry.content, justify: UserDefaults.standard.bool(forKey: "justifyArticle"))
+ let webView = WKWebView(frame: .zero)
+ webView.navigationDelegate = context.coordinator
+ webView.scrollView.delegate = context.coordinator
+ webView.isOpaque = false
+ webView.backgroundColor = .clear
+ webView.scrollView.backgroundColor = .clear
+ webView.scrollView.contentInsetAdjustmentBehavior = .always
+
+ webView.load(content: entry.titleHtml + (entry.content ?? ""), justify: UserDefaults.standard.bool(forKey: "justifyArticle"))
- return wkWebView
+ return webView
}
func updateUIView(_ webView: WKWebView, context _: Context) {
@@ -95,17 +113,18 @@ import WebKit
#if os(macOS)
struct WebView: NSViewRepresentable {
var entry: Entry
- private(set) var wkWebView = WKWebView(frame: .zero)
@EnvironmentObject var appSetting: AppSetting
+ @Binding var progress: Double
- func makeNSView(context _: Context) -> WKWebView {
- wkWebView.load(content: entry.content, justify: false)
+ func makeNSView(context: Context) -> WKWebView {
+ let webView = WKWebView(frame: .zero)
+ webView.navigationDelegate = context.coordinator
+ webView.load(content: entry.titleHtml + (entry.content ?? ""), justify: false)
- return wkWebView
+ return webView
}
func updateNSView(_ nsView: WKWebView, context _: Context) {
- nsView.load(content: entry.content, justify: false)
nsView.fontSizePercent(appSetting.webFontSizePercent)
}
@@ -186,10 +205,12 @@ struct WebView_Previews: PreviewProvider {
Group {
WebView(
entry: entry, progress: .constant(0.5)
- ).colorScheme(.light)
+ ).environmentObject(AppSetting())
+ .colorScheme(.light)
WebView(
entry: entry, progress: .constant(0.5)
- ).colorScheme(.dark)
+ ).environmentObject(AppSetting())
+ .colorScheme(.dark)
}
}
}
diff --git a/App/Features/Sync/RefreshButton.swift b/App/Features/Sync/RefreshButton.swift
index 036bc2c..0a34284 100644
--- a/App/Features/Sync/RefreshButton.swift
+++ b/App/Features/Sync/RefreshButton.swift
@@ -4,20 +4,15 @@ struct RefreshButton: View {
@Environment(AppSync.self) var appSync: AppSync
var body: some View {
- HStack {
+ ZStack {
if appSync.inProgress {
- ProgressView(value: appSync.progress, total: 100)
- #if os(iOS)
- .progressViewStyle(.linear)
- #else
+ ProgressView()
.progressViewStyle(.circular)
- #endif
} else {
Button(
action: appSync.requestSync,
label: {
Image(systemName: "arrow.counterclockwise")
- .frame(width: 34, height: 34, alignment: .center)
}
)
.buttonStyle(.plain)
@@ -26,6 +21,7 @@ struct RefreshButton: View {
.keyboardShortcut("r", modifiers: .command)
}
}
+ .frame(width: 34, height: 34, alignment: .center)
}
}
diff --git a/App/Resources/html-ressources/main.css b/App/Resources/html-ressources/main.css
index 1b2ad1a..1d825e2 100755
--- a/App/Resources/html-ressources/main.css
+++ b/App/Resources/html-ressources/main.css
@@ -14,7 +14,8 @@ html {
min-height: 100%;
}
-html, body {
+html,
+body {
overscroll-behavior: none;
}
@@ -31,16 +32,20 @@ a {
font-weight: bold;
}
-h2, h3, h4 {
+h2,
+h3,
+h4 {
font-family: 'PT Sans', sans-serif;
text-transform: uppercase;
}
-p, li {
+p,
+li {
color: #666;
}
-a:hover, a:focus {
+a:hover,
+a:focus {
text-decoration: none;
}
@@ -58,11 +63,11 @@ h2:after {
========================================================================== */
#main {
- margin-left: 13em;
- position: relative;
- z-index: 10;
- padding-right: 5%;
- padding-bottom: 1em;
+ margin-left: 13em;
+ position: relative;
+ z-index: 10;
+ padding-right: 5%;
+ padding-bottom: 1em;
}
#content {
@@ -86,7 +91,9 @@ blockquote {
margin: 0;
}
-#article h2, #article h3, #article h4 {
+#article h2,
+#article h3,
+#article h4 {
text-transform: none;
}
@@ -97,7 +104,7 @@ blockquote {
@media screen {
- body > header {
+ body>header {
background: #333;
position: fixed;
top: 0;
@@ -117,35 +124,45 @@ blockquote {
}
#article h1 {
- font-size: 1.2em;
+ font-size: 2.2em;
+ font-weight: 900;
+ line-height: 1.1;
+ margin-top: 1em;
+ margin-bottom: 0.8em;
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ line-clamp: 3;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
}
video {
- width: 100%
+ width: 100%
}
}
@media (prefers-color-scheme: dark) {
body {
- color: #FFF;
- background: #000;
+ color: #FFF;
+ background: #000;
}
::selection {
- color: #777;
- background: #000;
+ color: #777;
+ background: #000;
}
- p, li {
- color: #FFF;
+ p,
+ li {
+ color: #FFF;
}
a {
- color: #6CA0DC;
+ color: #6CA0DC;
}
blockquote {
- background: #111;
+ background: #111;
}
}
@@ -157,4 +174,4 @@ blockquote {
a {
color: #000;
}
-}
+}
\ No newline at end of file