perf: slim entries API + on-demand article loading for iOS Reader
Server: add slim query param (default true) to entries list endpoint, returning EntrySlimOut without content/full_content HTML — cuts payload size dramatically for list views. Single entry endpoint still returns full content. iOS: ArticleView now fetches full entry content on demand when opened instead of relying on list data. Shows loading indicator while fetching. Mark-as-read is fire-and-forget to avoid blocking the view. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,8 @@ struct ArticleView: View {
|
||||
@State private var isFetchingFull = false
|
||||
@State private var savedToBrain = false
|
||||
@State private var webViewHeight: CGFloat = 400
|
||||
@State private var isContentReady = false
|
||||
@State private var articleContent = ""
|
||||
|
||||
init(entry: ReaderEntry, vm: ReaderViewModel) {
|
||||
self.entry = entry
|
||||
@@ -53,7 +55,17 @@ struct ArticleView: View {
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
// Article body
|
||||
if currentEntry.articleHTML.isEmpty {
|
||||
if !isContentReady {
|
||||
HStack {
|
||||
ProgressView()
|
||||
.controlSize(.small)
|
||||
Text("Loading article...")
|
||||
.font(.caption)
|
||||
.foregroundStyle(Color.textTertiary)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 40)
|
||||
} else if articleContent.isEmpty {
|
||||
if isFetchingFull {
|
||||
HStack {
|
||||
ProgressView()
|
||||
@@ -82,7 +94,7 @@ struct ArticleView: View {
|
||||
.padding(.vertical, 40)
|
||||
}
|
||||
} else {
|
||||
ArticleWebView(html: wrapHTML(currentEntry.articleHTML), contentHeight: $webViewHeight)
|
||||
ArticleWebView(html: wrapHTML(articleContent), contentHeight: $webViewHeight)
|
||||
.frame(height: webViewHeight)
|
||||
}
|
||||
|
||||
@@ -142,8 +154,22 @@ struct ArticleView: View {
|
||||
}
|
||||
}
|
||||
.task {
|
||||
// Auto-mark as read
|
||||
await vm.markAsRead(entry)
|
||||
// Auto-mark as read (fire-and-forget)
|
||||
Task { await vm.markAsRead(entry) }
|
||||
|
||||
// Fetch full entry content on demand
|
||||
do {
|
||||
let fullEntry = try await ReaderAPI().getEntry(id: currentEntry.id)
|
||||
currentEntry = fullEntry
|
||||
articleContent = fullEntry.articleHTML
|
||||
if let idx = vm.entries.firstIndex(where: { $0.id == fullEntry.id }) {
|
||||
vm.entries[idx] = fullEntry
|
||||
}
|
||||
} catch {
|
||||
// Fall back to whatever content we already have
|
||||
articleContent = currentEntry.articleHTML
|
||||
}
|
||||
isContentReady = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +192,7 @@ struct ArticleView: View {
|
||||
do {
|
||||
let updated = try await ReaderAPI().fetchFullContent(entryId: currentEntry.id)
|
||||
currentEntry = updated
|
||||
articleContent = updated.articleHTML
|
||||
if let idx = vm.entries.firstIndex(where: { $0.id == updated.id }) {
|
||||
vm.entries[idx] = updated
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user