mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #547 from omnivore-app/fix/pending-content-loading
Pending content loading
This commit is contained in:
commit
ee8cae75cc
4 changed files with 42 additions and 37 deletions
|
|
@ -14,7 +14,7 @@ import WebKit
|
|||
@State var safariWebLink: SafariWebLink?
|
||||
@State private var navBarVisibilityRatio = 1.0
|
||||
@State private var showDeleteConfirmation = false
|
||||
@State private var showOverlay = true
|
||||
@State private var progressViewOpacity = 0.0
|
||||
@State var increaseFontActionID: UUID?
|
||||
@State var decreaseFontActionID: UUID?
|
||||
@State var annotationSaveTransactionID: UUID?
|
||||
|
|
@ -156,21 +156,6 @@ import WebKit
|
|||
annotationSaveTransactionID: $annotationSaveTransactionID,
|
||||
annotation: $annotation
|
||||
)
|
||||
.overlay(
|
||||
Group {
|
||||
if showOverlay {
|
||||
Color.systemBackground
|
||||
.transition(.opacity)
|
||||
.onAppear {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
|
||||
withAnimation(.linear(duration: 0.2)) {
|
||||
showOverlay = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.sheet(item: $safariWebLink) {
|
||||
SafariView(url: $0.url)
|
||||
}
|
||||
|
|
@ -186,9 +171,16 @@ import WebKit
|
|||
}
|
||||
)
|
||||
}
|
||||
} else if let errorMessage = viewModel.errorMessage {
|
||||
Text(errorMessage).padding()
|
||||
} else {
|
||||
Text(viewModel.contentFetchFailed ? "Unable to fetch content." : "Processing...")
|
||||
.padding()
|
||||
ProgressView()
|
||||
.opacity(progressViewOpacity)
|
||||
.onAppear {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000)) {
|
||||
progressViewOpacity = 1
|
||||
}
|
||||
}
|
||||
.task {
|
||||
await viewModel.loadContent(dataService: dataService, itemID: item.unwrappedID)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,24 @@ struct SafariWebLink: Identifiable {
|
|||
|
||||
@MainActor final class WebReaderViewModel: ObservableObject {
|
||||
@Published var articleContent: ArticleContent?
|
||||
@Published var contentFetchFailed = false
|
||||
@Published var errorMessage: String?
|
||||
|
||||
func loadContent(dataService: DataService, itemID: String) async {
|
||||
contentFetchFailed = false
|
||||
errorMessage = nil
|
||||
|
||||
do {
|
||||
articleContent = try await dataService.fetchArticleContent(itemID: itemID)
|
||||
} catch {
|
||||
contentFetchFailed = true
|
||||
if let fetchError = error as? ContentFetchError {
|
||||
switch fetchError {
|
||||
case .network:
|
||||
errorMessage = "We were unable to retrieve your content. Please ccheck network connectivity and try again."
|
||||
default:
|
||||
errorMessage = "We were unable to parse your content."
|
||||
}
|
||||
} else {
|
||||
errorMessage = "We were unable to retrieve your content."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import Foundation
|
||||
|
||||
public typealias ContentFetchError = SaveArticleError
|
||||
|
||||
public enum SaveArticleError: Error {
|
||||
case unauthorized
|
||||
case network
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ extension DataService {
|
|||
func prefetchPage(pendingLink: PendingLink, username: String) async {
|
||||
let content = try? await articleContent(username: username, itemID: pendingLink.itemID, useCache: false)
|
||||
|
||||
if content?.contentStatus == .processing, pendingLink.retryCount < 6 {
|
||||
if content?.contentStatus == .processing, pendingLink.retryCount < 7 {
|
||||
let retryDelayInNanoSeconds = UInt64(pendingLink.retryCount * 2 * 1_000_000_000)
|
||||
|
||||
do {
|
||||
|
|
@ -45,26 +45,24 @@ extension DataService {
|
|||
username: String? = nil,
|
||||
requestCount: Int = 1
|
||||
) async throws -> ArticleContent {
|
||||
guard let username = username ?? currentViewer?.username else {
|
||||
throw BasicError.message(messageText: "username could not be fetched from core data")
|
||||
guard requestCount < 7 else {
|
||||
throw ContentFetchError.badData
|
||||
}
|
||||
|
||||
guard let fetchedContent = try? await articleContent(username: username, itemID: itemID, useCache: true) else {
|
||||
throw BasicError.message(messageText: "networking error")
|
||||
guard let username = username ?? currentViewer?.username else {
|
||||
throw ContentFetchError.unauthorized
|
||||
}
|
||||
|
||||
let fetchedContent = try await articleContent(username: username, itemID: itemID, useCache: true)
|
||||
|
||||
switch fetchedContent.contentStatus {
|
||||
case .failed:
|
||||
throw BasicError.message(messageText: "content processing failed")
|
||||
throw ContentFetchError.badData
|
||||
case .processing:
|
||||
do {
|
||||
let retryDelayInNanoSeconds = UInt64(requestCount * 2 * 1_000_000_000)
|
||||
try await Task.sleep(nanoseconds: retryDelayInNanoSeconds)
|
||||
logger.debug("fetching content for \(itemID). request count: \(requestCount)")
|
||||
return try await fetchArticleContent(itemID: itemID, username: username, requestCount: requestCount + 1)
|
||||
} catch {
|
||||
throw BasicError.message(messageText: "content fetch failed")
|
||||
}
|
||||
let retryDelayInNanoSeconds = UInt64(requestCount * 2 * 1_000_000_000)
|
||||
try await Task.sleep(nanoseconds: retryDelayInNanoSeconds)
|
||||
logger.debug("fetching content for \(itemID). request count: \(requestCount)")
|
||||
return try await fetchArticleContent(itemID: itemID, username: username, requestCount: requestCount + 1)
|
||||
case .succeeded, .unknown:
|
||||
return fetchedContent
|
||||
}
|
||||
|
|
@ -120,7 +118,7 @@ extension DataService {
|
|||
return try await withCheckedThrowingContinuation { continuation in
|
||||
send(query, to: path, headers: headers) { [weak self] queryResult in
|
||||
guard let payload = try? queryResult.get() else {
|
||||
continuation.resume(throwing: BasicError.message(messageText: "network error"))
|
||||
continuation.resume(throwing: ContentFetchError.network)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +127,10 @@ extension DataService {
|
|||
// Default to suceeded since older links will return a nil status
|
||||
// (but the content is almost always there)
|
||||
let status = result.contentStatus ?? .succeeded
|
||||
if status == .failed {
|
||||
continuation.resume(throwing: ContentFetchError.badData)
|
||||
return
|
||||
}
|
||||
|
||||
if status == .succeeded {
|
||||
self?.persistArticleContent(
|
||||
|
|
@ -146,7 +148,7 @@ extension DataService {
|
|||
|
||||
continuation.resume(returning: articleContent)
|
||||
case .error:
|
||||
continuation.resume(throwing: BasicError.message(messageText: "LinkedItem fetch error"))
|
||||
continuation.resume(throwing: ContentFetchError.badData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue