diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Networking/Networker.swift b/apple/OmnivoreKit/Sources/Services/DataService/Networking/Networker.swift index 33f0e1da1..5da9691de 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Networking/Networker.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Networking/Networker.swift @@ -20,28 +20,6 @@ public final class Networker: NSObject, URLSessionTaskDelegate { self.appEnvironment = appEnvironment self.urlSession = .shared } - - public func createBackgroundSession() -> URLSession { - let sessionConfig = URLSessionConfiguration.background(withIdentifier: "app.omnivoreapp.BackgroundSessionConfig-") - sessionConfig.sharedContainerIdentifier = "group.app.omnivoreapp" - return URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil) - } - - public func urlSession(_: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - print("finished upload on original request", task.originalRequest, "error", error) - if let httpResponse = task.response as? HTTPURLResponse { - if 200 ... 299 ~= httpResponse.statusCode { - // success - if let requestId = task.originalRequest?.value(forHTTPHeaderField: "clientRequestId") { - print("COMPLETED UPLOADED REQUEST ID", requestId) - DispatchQueue.main.async { - NotificationCenter.default.post(name: NSNotification.LocallyCreatedItemSynced, object: nil, userInfo: ["objectID": requestId]) - } - } - } - print("DONE") - } - } } extension Networker { diff --git a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift index b53feb3bf..5f029fb1e 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift @@ -35,61 +35,81 @@ public extension DataService { } } -// func syncPdf(item: LinkedItem, usingSession session: URLSession) async throws -> Bool { -// try backgroundContext.performAndWait { -// item.serverSyncStatus = Int64(ServerSyncStatus.isSyncing.rawValue) -// try self.backgroundContext.save() -// } -// -// let id = item.unwrappedID -// let localPdfURL = item.localPdfURL -// let url = item.unwrappedPageURLString -// let uploadRequestUrl = try await uploadFileRequest(id: id, url: url) -// return await try uploadFile(id: id, localPdfURL: localPdfURL, url: uploadRequestUrl, usingSession: session) -// } + private func updateLinkedItemStatus(id: String, status: ServerSyncStatus) async throws { + try backgroundContext.performAndWait { + let fetchRequest: NSFetchRequest = LinkedItem.fetchRequest() + fetchRequest.predicate = NSPredicate(format: "id == %@", id) + + guard let linkedItem = (try? backgroundContext.fetch(fetchRequest))?.first else { return } + linkedItem.serverSyncStatus = Int64(status.rawValue) + } + } func syncPdf(id: String, localPdfURL: URL, url: String) async throws { -// try backgroundContext.performAndWait { -// item.serverSyncStatus = Int64(ServerSyncStatus.isSyncing.rawValue) -// try self.backgroundContext.save() -// } + do { + let uploadRequest = try await uploadFileRequest(id: id, url: url) + if let urlString = uploadRequest.urlString, let uploadUrl = URL(string: urlString) { + try await uploadFile(id: id, localPdfURL: localPdfURL, url: uploadUrl) + // try await services.dataService.saveFilePublisher(requestId: requestId, uploadFileId: uploadFileID, url: url) + } else { + throw SaveArticleError.badData + } - let uploadRequest = try await uploadFileRequest(id: id, url: url) - if let urlString = uploadRequest.urlString, let uploadUrl = URL(string: urlString) { - try await uploadFile(id: id, localPdfURL: localPdfURL, url: uploadUrl) - // try await services.dataService.saveFilePublisher(requestId: requestId, uploadFileId: uploadFileID, url: url) - } else { - throw SaveArticleError.badData + try await updateLinkedItemStatus(id: id, status: .isNSync) + try backgroundContext.performAndWait { + try backgroundContext.save() + } + } catch { + backgroundContext.rollback() + throw error } } func syncPage(id: String, originalHtml: String, title: String?, url: String) async throws { - // try backgroundContext.performAndWait { - // item.serverSyncStatus = Int64(ServerSyncStatus.isSyncing.rawValue) - // try self.backgroundContext.save() - // } - try await savePage(id: id, url: url, title: title ?? url, originalHtml: originalHtml) + do { + try await savePage(id: id, url: url, title: title ?? url, originalHtml: originalHtml) + try await updateLinkedItemStatus(id: id, status: .isNSync) + try backgroundContext.performAndWait { + try backgroundContext.save() + } + } catch { + backgroundContext.performAndWait { + backgroundContext.rollback() + } + throw error + } } func syncUrl(id: String, url: String) async throws { - try await saveURL(id: id, url: url) + do { + try await updateLinkedItemStatus(id: id, status: .isSyncing) + try await saveURL(id: id, url: url) + try backgroundContext.performAndWait { + try backgroundContext.save() + } + } catch { + backgroundContext.performAndWait { + backgroundContext.rollback() + } + throw error + } } func syncLocalCreatedLinkedItem(item: LinkedItem) { switch item.contentReader { case "PDF": -// let id = item.unwrappedID -// let localPdfURL = item.localPdfURL -// let url = item.unwrappedPageURLString -// Task { -// let uploadRequestUrl = try await uploadFileRequest(id: id, url: url) -// uploadFile(id: id, localPdfURL: localPdfURL, url: uploadRequestUrl) -// try await backgroundContext.perform { -// item.serverSyncStatus = Int64(ServerSyncStatus.isNSync.rawValue) -// try self.backgroundContext.save() -// } -// } - break + let id = item.unwrappedID + let localPdfURL = item.localPdfURL + let url = item.unwrappedPageURLString + + if let pdfUrlStr = localPdfURL, let localPdfURL = URL(string: pdfUrlStr) { + Task { + try await syncPdf(id: id, localPdfURL: localPdfURL, url: url) + } + } else { + // TODO: This is an invalid object, we should have a way of reflecting that with an error state + // updateLinkedItemStatus(id: id, status: .) + } case "WEB": let id = item.unwrappedID let url = item.unwrappedPageURLString @@ -98,13 +118,9 @@ public extension DataService { Task { if let originalHtml = originalHtml { - try await savePage(id: id, url: url, title: title, originalHtml: originalHtml) + try await syncPage(id: id, originalHtml: originalHtml, title: title, url: url) } else { - try await saveURL(id: id, url: url) - } - try await backgroundContext.perform { - item.serverSyncStatus = Int64(ServerSyncStatus.isNSync.rawValue) - try self.backgroundContext.save() + try await syncUrl(id: id, url: url) } } default: diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift index a8ac006c1..c67f7e43c 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift @@ -85,6 +85,9 @@ extension DataService { return cachedContent } + // If the page was locally created, make sure they are synced before we pull content + await syncUnsyncedArticleContent(itemID: itemID) + enum QueryResult { case success(result: ArticleProps) case error(error: String) @@ -304,6 +307,49 @@ extension DataService { ) } } + + func syncUnsyncedArticleContent(itemID: String) async { + let linkedItemFetchRequest: NSFetchRequest = LinkedItem.fetchRequest() + linkedItemFetchRequest.predicate = NSPredicate( + format: "id == %@", itemID + ) + + let context = backgroundContext + + var id: String? + var url: String? + var title: String? + var originalHtml: String? + var serverSyncStatus: Int64? + + backgroundContext.performAndWait { + guard let linkedItem = try? context.fetch(linkedItemFetchRequest).first else { return } + id = linkedItem.unwrappedID + url = linkedItem.unwrappedPageURLString + title = linkedItem.unwrappedTitle + originalHtml = linkedItem.originalHtml + serverSyncStatus = linkedItem.serverSyncStatus + } + + if let id = id, let url = url, let title = title, + let serverSyncStatus = serverSyncStatus, + serverSyncStatus != ServerSyncStatus.isNSync.rawValue + { + do { + if let originalHtml = originalHtml { + try await savePage(id: id, url: url, title: title, originalHtml: originalHtml) + } else { + try await saveURL(id: id, url: url) + } + try backgroundContext.performAndWait { + try backgroundContext.save() + } + } catch { + // We don't propogate these errors, we just let it pass through so + // the user can attempt to fetch content again. + } + } + } } private extension ArticleContentStatus {