From 7fe3fc75c0231f6464f91fdd4cf8fe9507d1fe27 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Sun, 5 Jun 2022 09:09:08 -0700 Subject: [PATCH 01/29] Dont show the cloud icon while page scraping is running --- .../Sources/Views/ShareExtensionView.swift | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift b/apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift index 1fc6557ab..6678e7fe5 100644 --- a/apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift +++ b/apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift @@ -178,8 +178,10 @@ public struct ShareExtensionChildView: View { private var cloudIconColor: Color { switch viewModel.status { - case .saved, .processing: + case .saved: return .appGrayText + case .processing: + return .clear case .failed(error: _), .syncFailed(error: _): return .red case .synced: @@ -188,12 +190,8 @@ public struct ShareExtensionChildView: View { } private func localImage(from: URL) -> Image? { - do { - if let data = try? Data(contentsOf: from), let img = UIImage(data: data) { - return Image(uiImage: img) - } - } catch { - return nil + if let data = try? Data(contentsOf: from), let img = UIImage(data: data) { + return Image(uiImage: img) } return nil } From 85cbd4e9d0b45972769a16b840a6fc2c9fc9935d Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Sun, 5 Jun 2022 10:44:23 -0700 Subject: [PATCH 02/29] Handle errors updating elastic when saving pages --- .../Sources/Views/SyncingIcon.swift | 8 ++++ packages/api/src/services/save_page.ts | 39 ++++++++++++++----- 2 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 apple/OmnivoreKit/Sources/Views/SyncingIcon.swift diff --git a/apple/OmnivoreKit/Sources/Views/SyncingIcon.swift b/apple/OmnivoreKit/Sources/Views/SyncingIcon.swift new file mode 100644 index 000000000..a234eb638 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/SyncingIcon.swift @@ -0,0 +1,8 @@ +// +// File.swift +// +// +// Created by Jackson Harper on 6/5/22. +// + +import Foundation diff --git a/packages/api/src/services/save_page.ts b/packages/api/src/services/save_page.ts index f56f95884..4d7315a95 100644 --- a/packages/api/src/services/save_page.ts +++ b/packages/api/src/services/save_page.ts @@ -1,6 +1,11 @@ import { PubsubClient } from '../datalayer/pubsub' import { homePageURL } from '../env' -import { Maybe, SavePageInput, SaveResult } from '../generated/graphql' +import { + Maybe, + SaveErrorCode, + SavePageInput, + SaveResult, +} from '../generated/graphql' import { DataModels } from '../resolvers/types' import { generateSlug, stringToHash, validatedDate } from '../utils/helpers' import { parsePreparedContent } from '../utils/parser' @@ -102,14 +107,22 @@ export const savePage = async ( state: ArticleSavingRequestStatus.Succeeded, }) if (existingPage) { - await updatePage( - existingPage.id, - { - savedAt: new Date(), - archivedAt: undefined, - }, - ctx - ) + if ( + !(await updatePage( + existingPage.id, + { + savedAt: new Date(), + archivedAt: undefined, + }, + ctx + )) + ) { + console.log('FAILED TO UPDATE EXISTING PAGE WITH', input) + return { + errorCodes: [SaveErrorCode.Unknown], + message: 'Failed to update existing page', + } + } input.clientRequestId = existingPage.id } else if (shouldParseInBackend(input)) { await createPageSaveRequest( @@ -120,7 +133,13 @@ export const savePage = async ( input.clientRequestId ) } else { - await createPage(articleToSave, ctx) + if (!(await createPage(articleToSave, ctx))) { + console.log('FAILED TO CREATE PAGE WITH INPUT', input) + return { + errorCodes: [SaveErrorCode.Unknown], + message: 'Failed to create new page', + } + } } return { From 0efbfe6f139cde4a3c0a5eff3f534e50969ca7c0 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Sun, 5 Jun 2022 11:10:21 -0700 Subject: [PATCH 03/29] Better variable name --- apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift b/apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift index 6678e7fe5..afa165acc 100644 --- a/apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift +++ b/apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift @@ -189,8 +189,8 @@ public struct ShareExtensionChildView: View { } } - private func localImage(from: URL) -> Image? { - if let data = try? Data(contentsOf: from), let img = UIImage(data: data) { + private func localImage(from url: URL) -> Image? { + if let data = try? Data(contentsOf: url), let img = UIImage(data: data) { return Image(uiImage: img) } return nil From 9baeb89742654eac39e5bf52d137b209c6f57e39 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Sun, 5 Jun 2022 11:47:43 -0700 Subject: [PATCH 04/29] Add more elastic debugging --- packages/api/src/elastic/pages.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/elastic/pages.ts b/packages/api/src/elastic/pages.ts index 4640c9747..0f0e69f74 100644 --- a/packages/api/src/elastic/pages.ts +++ b/packages/api/src/elastic/pages.ts @@ -188,7 +188,7 @@ export const createPage = async ( return body._id as string } catch (e) { - console.error('failed to create a page in elastic', e) + console.error('failed to create a page in elastic', JSON.stringify(e)) return undefined } } From bf2c0d6b6622ac588a36907cce0a8d4f2bced54c Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Sun, 5 Jun 2022 11:53:11 -0700 Subject: [PATCH 05/29] WIP ios syncing improvements --- .../DataService/Mutations/SavePage.swift | 14 ++++-- .../Services/DataService/OfflineSync.swift | 23 ++-------- .../Queries/ArticleContentQuery.swift | 1 + .../Sources/Views/FeedItem/GridCard.swift | 4 ++ .../Views/FeedItem/HomeFeedCardView.swift | 3 +- .../Sources/Views/SyncingIcon.swift | 44 ++++++++++++++++++- 6 files changed, 63 insertions(+), 26 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePage.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePage.swift index 57c2b3e79..f8b427b37 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePage.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePage.swift @@ -3,7 +3,7 @@ import Models import SwiftGraphQL public extension DataService { - func savePage(id: String, url: String, title: String, originalHtml: String) async throws { + func savePage(id: String, url: String, title: String, originalHtml: String) async throws -> String? { enum MutationResult { case saved(requestId: String, url: String) case error(errorCode: Enums.SaveErrorCode) @@ -20,7 +20,13 @@ public extension DataService { let selection = Selection { try $0.on( saveError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unknown) }, - saveSuccess: .init { .saved(requestId: id, url: (try? $0.url()) ?? "") } + saveSuccess: .init { + if let requestId = try? $0.clientRequestId(), let url = try? $0.url() { + return .saved(requestId: requestId, url: url) + } else { + return .error(errorCode: .unknown) + } + } ) } @@ -42,8 +48,8 @@ public extension DataService { return } switch payload.data { - case .saved: - continuation.resume() + case let .saved(requestId: requestId, url: _): + continuation.resume(returning: requestId) case let .error(errorCode: errorCode): switch errorCode { case .unauthorized: diff --git a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift index 4950b7c9d..d92ce1631 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift @@ -69,12 +69,14 @@ public extension DataService { func syncPage(id: String, originalHtml: String, title: String?, url: String) async throws { do { - try await savePage(id: id, url: url, title: title ?? url, originalHtml: originalHtml) + let newId = try await savePage(id: id, url: url, title: title ?? url, originalHtml: originalHtml) + print("NEW ID FOR ITEM", newId, "FROM OLD ID", id) try await updateLinkedItemStatus(id: id, status: .isNSync) try backgroundContext.performAndWait { try backgroundContext.save() } } catch { + print("ERROR SYNCING PAGE", error) backgroundContext.performAndWait { backgroundContext.rollback() } @@ -186,23 +188,4 @@ public extension DataService { } } } - - @objc - func locallyCreatedItemSynced(notification: NSNotification) { - print("SYNCED LOCALLY CREATED ITEM", notification) - if let objectId = notification.userInfo?["objectID"] as? String { - do { - try backgroundContext.performAndWait { - let fetchRequest: NSFetchRequest = LinkedItem.fetchRequest() - fetchRequest.predicate = NSPredicate(format: "id == %@", objectId) - if let existingItem = try? self.backgroundContext.fetch(fetchRequest).first { - existingItem.serverSyncStatus = Int64(ServerSyncStatus.isNSync.rawValue) - try self.backgroundContext.save() - } - } - } catch { - print("ERROR", error) - } - } - } } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift index 89da84ecd..85fe53a79 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift @@ -332,6 +332,7 @@ extension DataService { serverSyncStatus = linkedItem.serverSyncStatus } + print("SERVER SYNC STATUS FOR LOADING ITEM", serverSyncStatus) if let id = id, let url = url, let title = title, let serverSyncStatus = serverSyncStatus, serverSyncStatus != ServerSyncStatus.isNSync.rawValue diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift index fff4cc6d1..bb736984f 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift @@ -161,6 +161,10 @@ public struct GridCard: View { } .onTapGesture { tapHandler() } } + + if let status = item.serverSyncStatus, status != ServerSyncStatus.isNSync.rawValue { + SyncStatusIcon(status: ServerSyncStatus(rawValue: Int(status)) ?? ServerSyncStatus.isNSync) + } } .padding(.horizontal, 0) .padding(.top, 0) diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift index 1a39ef7f7..fa62c73fa 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift @@ -66,7 +66,7 @@ public struct FeedCard: View { } } - if item.sortedLabels.count > 0 { + if item.hasLabels { // Category Labels ScrollView(.horizontal, showsIndicators: false) { HStack { @@ -93,5 +93,6 @@ public struct FeedCard: View { maxHeight: nil, alignment: .topLeading ) + .overlay(SyncStatusIcon(status: ServerSyncStatus(rawValue: Int(item.serverSyncStatus)) ?? ServerSyncStatus.isNSync), alignment: .bottomTrailing) } } diff --git a/apple/OmnivoreKit/Sources/Views/SyncingIcon.swift b/apple/OmnivoreKit/Sources/Views/SyncingIcon.swift index a234eb638..de0dda106 100644 --- a/apple/OmnivoreKit/Sources/Views/SyncingIcon.swift +++ b/apple/OmnivoreKit/Sources/Views/SyncingIcon.swift @@ -1,8 +1,50 @@ // // File.swift -// +// // // Created by Jackson Harper on 6/5/22. // import Foundation +import Models +import SwiftUI +import Utils + +public struct SyncStatusIcon: View { + let status: ServerSyncStatus + + init(status: ServerSyncStatus) { + self.status = status + } + + private var cloudIconName: String { + switch status { +// case .isNSync: +// return "checkmark.icloud" + case .isNSync: + return "exclamationmark.icloud" + case .isSyncing, .needsCreation, .needsDeletion, .needsUpdate: + return "icloud" + } + } + + private var cloudIconColor: Color { + switch status { +// case .isNSync: +// return .blue + case .isNSync: + return .red + case .isSyncing, .needsCreation, .needsDeletion, .needsUpdate: + return .appGrayText + } + } + + public var body: some View { + Image(systemName: cloudIconName) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 12, height: 12, alignment: .trailing) + .foregroundColor(cloudIconColor) + .padding(EdgeInsets(top: 0, leading: 0, bottom: 8, trailing: 8)) + } +} From b107a86ba9c5cd05256c4fe3a4a07c6c95a639cf Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Sun, 5 Jun 2022 12:43:10 -0700 Subject: [PATCH 06/29] Handle response ids when saving items This will be used when handling pages that change their IDs when saved. --- .../DataService/Mutations/SavePDF.swift | 15 +++++++++----- .../DataService/Mutations/SaveUrl.swift | 14 +++++++++---- .../Services/DataService/OfflineSync.swift | 20 ++++++++++++------- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePDF.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePDF.swift index 22017a74d..43cd10ef6 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePDF.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePDF.swift @@ -110,8 +110,7 @@ public extension DataService { } } - // swiftlint:disable:next line_length - func saveFilePublisher(requestId: String, uploadFileId: String, url: String) async throws { + func saveFilePublisher(requestId: String, uploadFileId: String, url: String) async throws -> String? { enum MutationResult { case saved(requestId: String, url: String) case error(errorCode: Enums.SaveErrorCode) @@ -127,7 +126,13 @@ public extension DataService { let selection = Selection { try $0.on( saveError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unknown) }, - saveSuccess: .init { .saved(requestId: requestId, url: (try? $0.url()) ?? "") } + saveSuccess: .init { + if let requestId = try? $0.clientRequestId(), let url = try? $0.url() { + return .saved(requestId: requestId, url: url) + } else { + return .error(errorCode: .unknown) + } + } ) } @@ -148,8 +153,8 @@ public extension DataService { } switch payload.data { - case .saved: - continuation.resume() + case let .saved(requestId: requestId, url: _): + continuation.resume(returning: requestId) case let .error(errorCode: errorCode): switch errorCode { case .unauthorized: diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SaveUrl.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SaveUrl.swift index 34ac0ddf3..ade5c1f5d 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SaveUrl.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SaveUrl.swift @@ -3,7 +3,7 @@ import Models import SwiftGraphQL public extension DataService { - func saveURL(id: String, url: String) async throws { + func saveURL(id: String, url: String) async throws -> String? { enum MutationResult { case saved(requestId: String, url: String) case error(errorCode: Enums.SaveErrorCode) @@ -18,7 +18,13 @@ public extension DataService { let selection = Selection { try $0.on( saveError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unknown) }, - saveSuccess: .init { .saved(requestId: id, url: (try? $0.url()) ?? "") } + saveSuccess: .init { + if let requestId = try? $0.clientRequestId(), let url = try? $0.url() { + return .saved(requestId: requestId, url: url) + } else { + return .error(errorCode: .unknown) + } + } ) } @@ -41,8 +47,8 @@ public extension DataService { } switch payload.data { - case .saved: - continuation.resume() + case let .saved(requestId: requestId, url: _): + continuation.resume(returning: requestId) case let .error(errorCode: errorCode): switch errorCode { case .unauthorized: diff --git a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift index d92ce1631..b3b74e7b0 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift @@ -35,18 +35,22 @@ public extension DataService { } } - private func updateLinkedItemStatus(id: String, status: ServerSyncStatus) async throws { + private func updateLinkedItemStatus(id: String, newId _: 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 } + // TODO: handle item id changes + // linkedItem.id = linkedItem.serverSyncStatus = Int64(status.rawValue) } } func syncPdf(id: String, localPdfURL: URL, url: String) async throws { do { + try await updateLinkedItemStatus(id: id, newId: nil, status: .isSyncing) + 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) @@ -55,7 +59,7 @@ public extension DataService { throw SaveArticleError.badData } - try await updateLinkedItemStatus(id: id, status: .isNSync) + try await updateLinkedItemStatus(id: id, newId: nil, status: .isNSync) try backgroundContext.performAndWait { try backgroundContext.save() } @@ -69,14 +73,14 @@ public extension DataService { func syncPage(id: String, originalHtml: String, title: String?, url: String) async throws { do { + try await updateLinkedItemStatus(id: id, newId: nil, status: .isSyncing) + let newId = try await savePage(id: id, url: url, title: title ?? url, originalHtml: originalHtml) - print("NEW ID FOR ITEM", newId, "FROM OLD ID", id) - try await updateLinkedItemStatus(id: id, status: .isNSync) + try await updateLinkedItemStatus(id: id, newId: newId, status: .isNSync) try backgroundContext.performAndWait { try backgroundContext.save() } } catch { - print("ERROR SYNCING PAGE", error) backgroundContext.performAndWait { backgroundContext.rollback() } @@ -86,8 +90,10 @@ public extension DataService { func syncUrl(id: String, url: String) async throws { do { - try await updateLinkedItemStatus(id: id, status: .isSyncing) - try await saveURL(id: id, url: url) + try await updateLinkedItemStatus(id: id, newId: nil, status: .isSyncing) + + let newId = try await saveURL(id: id, url: url) + try await updateLinkedItemStatus(id: id, newId: newId, status: .isNSync) try backgroundContext.performAndWait { try backgroundContext.save() } From 3136705a75953b3ab6d9f0f23707f66600d5aea6 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Sun, 5 Jun 2022 12:50:05 -0700 Subject: [PATCH 07/29] Handle errors when creating a page save request on savePage --- packages/api/src/services/save_page.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/api/src/services/save_page.ts b/packages/api/src/services/save_page.ts index 4d7315a95..a973ecf23 100644 --- a/packages/api/src/services/save_page.ts +++ b/packages/api/src/services/save_page.ts @@ -117,7 +117,6 @@ export const savePage = async ( ctx )) ) { - console.log('FAILED TO UPDATE EXISTING PAGE WITH', input) return { errorCodes: [SaveErrorCode.Unknown], message: 'Failed to update existing page', @@ -125,16 +124,22 @@ export const savePage = async ( } input.clientRequestId = existingPage.id } else if (shouldParseInBackend(input)) { - await createPageSaveRequest( - saver.userId, - input.url, - ctx.models, - ctx.pubsub, - input.clientRequestId - ) + try { + await createPageSaveRequest( + saver.userId, + input.url, + ctx.models, + ctx.pubsub, + input.clientRequestId + ) + } catch (e) { + return { + errorCodes: [SaveErrorCode.Unknown], + message: 'Failed to create page save request', + } + } } else { if (!(await createPage(articleToSave, ctx))) { - console.log('FAILED TO CREATE PAGE WITH INPUT', input) return { errorCodes: [SaveErrorCode.Unknown], message: 'Failed to create new page', From 8d271daf38a139eeb9ee789024befe0ef9dc36ad Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 6 Jun 2022 12:35:44 -0700 Subject: [PATCH 08/29] Only attempt to force sync content if it needs creation before reading --- .../Services/DataService/Queries/ArticleContentQuery.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift index 85fe53a79..d612aa82e 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift @@ -335,7 +335,7 @@ extension DataService { print("SERVER SYNC STATUS FOR LOADING ITEM", serverSyncStatus) if let id = id, let url = url, let title = title, let serverSyncStatus = serverSyncStatus, - serverSyncStatus != ServerSyncStatus.isNSync.rawValue + serverSyncStatus == ServerSyncStatus.needsCreation.rawValue { do { if let originalHtml = originalHtml { From 754db717ccb52c2fd31274f2819edb4767a1bc09 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 6 Jun 2022 12:36:07 -0700 Subject: [PATCH 09/29] Used savedat instead of createdat for sort descriptors --- apple/OmnivoreKit/Sources/Models/LinkedItemSort.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Models/LinkedItemSort.swift b/apple/OmnivoreKit/Sources/Models/LinkedItemSort.swift index ba0919203..0c5efd1fa 100644 --- a/apple/OmnivoreKit/Sources/Models/LinkedItemSort.swift +++ b/apple/OmnivoreKit/Sources/Models/LinkedItemSort.swift @@ -42,9 +42,9 @@ public extension LinkedItemSort { var sortDescriptors: [NSSortDescriptor] { switch self { case .newest /* , .relevance */: - return [NSSortDescriptor(keyPath: \LinkedItem.createdAt, ascending: false)] + return [NSSortDescriptor(keyPath: \LinkedItem.savedAt, ascending: false)] case .oldest: - return [NSSortDescriptor(keyPath: \LinkedItem.createdAt, ascending: true)] + return [NSSortDescriptor(keyPath: \LinkedItem.savedAt, ascending: true)] // case .recentlyRead: // return [NSSortDescriptor(keyPath: \LinkedItem.updatedAt, ascending: false)] case .recentlyPublished: From 311c5172cc7720254422c40c0a2c777c1a0d44d4 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 6 Jun 2022 22:30:01 -0700 Subject: [PATCH 10/29] Status is moved to the child view model now --- .../App/AppExtensions/Share/ShareExtensionScene.swift | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift index a5ea021e1..8e28686a9 100644 --- a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift +++ b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift @@ -22,7 +22,6 @@ public extension PlatformViewController { public class ShareExtensionViewModel: ObservableObject { @Published var title: String? - @Published var status: ShareExtensionStatus = .processing @Published var debugText: String? let saveService = ExtensionSaveService() @@ -42,13 +41,9 @@ public class ShareExtensionViewModel: ObservableObject { if let extensionContext = extensionContext { saveService.save(extensionContext, requestId: requestId, shareExtensionViewModel: shareExtensionViewModel) } else { - updateStatus(.failed(error: .unknown(description: "Internal Error"))) - } - } - - private func updateStatus(_ newStatus: ShareExtensionStatus) { - DispatchQueue.main.async { - self.status = newStatus + DispatchQueue.main.async { + shareExtensionViewModel.status = .failed(error: .unknown(description: "Internal Error")) + } } } } From 5315d1a4ace328bc82726a210d1a7ce03b29789a Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 6 Jun 2022 22:30:29 -0700 Subject: [PATCH 11/29] Update link item IDs if they are changed --- .../Sources/Services/DataService/OfflineSync.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift index b3b74e7b0..53574fa37 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift @@ -35,14 +35,15 @@ public extension DataService { } } - private func updateLinkedItemStatus(id: String, newId _: String?, status: ServerSyncStatus) async throws { - try backgroundContext.performAndWait { + private func updateLinkedItemStatus(id: String, newId: String?, status: ServerSyncStatus) async throws { + backgroundContext.performAndWait { let fetchRequest: NSFetchRequest = LinkedItem.fetchRequest() fetchRequest.predicate = NSPredicate(format: "id == %@", id) guard let linkedItem = (try? backgroundContext.fetch(fetchRequest))?.first else { return } - // TODO: handle item id changes - // linkedItem.id = + if let newId = newId { + linkedItem.id = newId + } linkedItem.serverSyncStatus = Int64(status.rawValue) } } From 9ab2f1c991f0aba4734b30c03dc3320ab46b4be8 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 6 Jun 2022 22:49:17 -0700 Subject: [PATCH 12/29] Remove sync status button until we have failures --- apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift index fa62c73fa..f7515393c 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift @@ -93,6 +93,5 @@ public struct FeedCard: View { maxHeight: nil, alignment: .topLeading ) - .overlay(SyncStatusIcon(status: ServerSyncStatus(rawValue: Int(item.serverSyncStatus)) ?? ServerSyncStatus.isNSync), alignment: .bottomTrailing) } } From 9e88a11ddabaa37a1800aab881b93c491e8fa909 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 6 Jun 2022 22:49:49 -0700 Subject: [PATCH 13/29] Enable Read Now, handle scenarios where an article is already saved so the LinkedItem.id is updated --- .../Share/ExtensionSaveService.swift | 42 +++++++++++-------- .../Share/ShareExtensionScene.swift | 7 ++-- .../Services/DataService/OfflineSync.swift | 6 ++- .../Sources/Views/ShareExtensionView.swift | 22 +++++----- 4 files changed, 44 insertions(+), 33 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ExtensionSaveService.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ExtensionSaveService.swift index c3c7faf3c..1350d936a 100644 --- a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ExtensionSaveService.swift +++ b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ExtensionSaveService.swift @@ -18,7 +18,7 @@ class ExtensionSaveService { self.queue = OperationQueue() } - private func queueSaveOperation(_ pageScrape: PageScrapePayload, requestId: String, shareExtensionViewModel: ShareExtensionChildViewModel) { + private func queueSaveOperation(_ pageScrape: PageScrapePayload, shareExtensionViewModel: ShareExtensionChildViewModel) { ProcessInfo().performExpiringActivity(withReason: "app.omnivore.SaveActivity") { [self] expiring in guard !expiring else { self.queue.cancelAllOperations() @@ -26,14 +26,14 @@ class ExtensionSaveService { return } - let operation = SaveOperation(pageScrapePayload: pageScrape, requestId: requestId, shareExtensionViewModel: shareExtensionViewModel) + let operation = SaveOperation(pageScrapePayload: pageScrape, shareExtensionViewModel: shareExtensionViewModel) self.queue.addOperation(operation) self.queue.waitUntilAllOperationsAreFinished() } } - public func save(_ extensionContext: NSExtensionContext, requestId: String, shareExtensionViewModel: ShareExtensionChildViewModel) { + public func save(_ extensionContext: NSExtensionContext, shareExtensionViewModel: ShareExtensionChildViewModel) { PageScraper.scrape(extensionContext: extensionContext) { [weak self] result in guard let self = self else { return } @@ -68,8 +68,8 @@ class ExtensionSaveService { } } } - self.queueSaveOperation(payload, requestId: requestId, shareExtensionViewModel: shareExtensionViewModel) - case let .failure: + self.queueSaveOperation(payload, shareExtensionViewModel: shareExtensionViewModel) + case .failure: DispatchQueue.main.async { shareExtensionViewModel.status = .failed(error: .unknown(description: "Could not retrieve content")) } @@ -78,7 +78,6 @@ class ExtensionSaveService { } class SaveOperation: Operation, URLSessionDelegate { - let requestId: String let services: Services let pageScrapePayload: PageScrapePayload let shareExtensionViewModel: ShareExtensionChildViewModel @@ -92,9 +91,8 @@ class ExtensionSaveService { case finished } - init(pageScrapePayload: PageScrapePayload, requestId: String, shareExtensionViewModel: ShareExtensionChildViewModel) { + init(pageScrapePayload: PageScrapePayload, shareExtensionViewModel: ShareExtensionChildViewModel) { self.pageScrapePayload = pageScrapePayload - self.requestId = requestId self.shareExtensionViewModel = shareExtensionViewModel self.state = .created @@ -138,7 +136,7 @@ class ExtensionSaveService { queue = OperationQueue() Task { - await persist(services: self.services, pageScrapePayload: self.pageScrapePayload, requestId: self.requestId) + await persist(services: self.services, pageScrapePayload: self.pageScrapePayload) } } @@ -146,38 +144,48 @@ class ExtensionSaveService { super.cancel() } - private func updateStatus(newStatus: ShareExtensionStatus) { + private func updateStatus(_ requestId: String?, newStatus: ShareExtensionStatus) { DispatchQueue.main.async { self.shareExtensionViewModel.status = newStatus + if let requestId = requestId { + self.shareExtensionViewModel.requestId = requestId + } } } - private func persist(services: Services, pageScrapePayload: PageScrapePayload, requestId: String) async { + private func persist(services: Services, pageScrapePayload: PageScrapePayload) async { + var requestId = shareExtensionViewModel.requestId + do { try await services.dataService.persistPageScrapePayload(pageScrapePayload, requestId: requestId) } catch { - updateStatus(newStatus: .failed(error: SaveArticleError.unknown(description: "Unable to access content"))) + updateStatus(nil, newStatus: .failed(error: SaveArticleError.unknown(description: "Unable to access content"))) return } do { - updateStatus(newStatus: .saved) + updateStatus(requestId, newStatus: .saved) switch pageScrapePayload.contentType { case .none: - try await services.dataService.syncUrl(id: requestId, url: pageScrapePayload.url) + requestId = try await services.dataService.syncUrl(id: requestId, url: pageScrapePayload.url) case let .pdf(localUrl): try await services.dataService.syncPdf(id: requestId, localPdfURL: localUrl, url: pageScrapePayload.url) case let .html(html, title, _): - try await services.dataService.syncPage(id: requestId, originalHtml: html, title: title, url: pageScrapePayload.url) + requestId = try await services.dataService.syncPage( + id: requestId, + originalHtml: html, + title: title, + url: pageScrapePayload.url + ) } } catch { - updateStatus(newStatus: .syncFailed(error: SaveArticleError.unknown(description: "Unknown Error"))) + updateStatus(nil, newStatus: .syncFailed(error: SaveArticleError.unknown(description: "Unknown Error"))) return } - updateStatus(newStatus: .synced) + updateStatus(requestId, newStatus: .synced) state = .finished } } diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift index 8e28686a9..467d158f7 100644 --- a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift +++ b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift @@ -25,9 +25,8 @@ public class ShareExtensionViewModel: ObservableObject { @Published var debugText: String? let saveService = ExtensionSaveService() - let requestId = UUID().uuidString.lowercased() - func handleReadNowAction(extensionContext: NSExtensionContext?) { + func handleReadNowAction(requestId: String, extensionContext: NSExtensionContext?) { #if os(iOS) if let application = UIApplication.value(forKeyPath: #keyPath(UIApplication.shared)) as? UIApplication { let deepLinkUrl = NSURL(string: "omnivore://shareExtensionRequestID/\(requestId)") @@ -39,7 +38,7 @@ public class ShareExtensionViewModel: ObservableObject { func savePage(extensionContext: NSExtensionContext?, shareExtensionViewModel: ShareExtensionChildViewModel) { if let extensionContext = extensionContext { - saveService.save(extensionContext, requestId: requestId, shareExtensionViewModel: shareExtensionViewModel) + saveService.save(extensionContext, shareExtensionViewModel: shareExtensionViewModel) } else { DispatchQueue.main.async { shareExtensionViewModel.status = .failed(error: .unknown(description: "Internal Error")) @@ -57,7 +56,7 @@ struct ShareExtensionView: View { ShareExtensionChildView( viewModel: childViewModel, onAppearAction: { viewModel.savePage(extensionContext: extensionContext, shareExtensionViewModel: childViewModel) }, - readNowButtonAction: { viewModel.handleReadNowAction(extensionContext: extensionContext) }, + readNowButtonAction: { viewModel.handleReadNowAction(requestId: $0, extensionContext: extensionContext) }, dismissButtonTappedAction: { _, _ in extensionContext?.completeRequest(returningItems: [], completionHandler: nil) } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift index 53574fa37..8a9239957 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift @@ -72,7 +72,7 @@ public extension DataService { } } - func syncPage(id: String, originalHtml: String, title: String?, url: String) async throws { + func syncPage(id: String, originalHtml: String, title: String?, url: String) async throws -> String { do { try await updateLinkedItemStatus(id: id, newId: nil, status: .isSyncing) @@ -81,6 +81,7 @@ public extension DataService { try backgroundContext.performAndWait { try backgroundContext.save() } + return newId ?? id } catch { backgroundContext.performAndWait { backgroundContext.rollback() @@ -89,7 +90,7 @@ public extension DataService { } } - func syncUrl(id: String, url: String) async throws { + func syncUrl(id: String, url: String) async throws -> String { do { try await updateLinkedItemStatus(id: id, newId: nil, status: .isSyncing) @@ -98,6 +99,7 @@ public extension DataService { try backgroundContext.performAndWait { try backgroundContext.save() } + return newId ?? id } catch { backgroundContext.performAndWait { backgroundContext.rollback() diff --git a/apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift b/apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift index afa165acc..8d6f4774d 100644 --- a/apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift +++ b/apple/OmnivoreKit/Sources/Views/ShareExtensionView.swift @@ -7,8 +7,11 @@ public class ShareExtensionChildViewModel: ObservableObject { @Published public var title: String? @Published public var url: String? @Published public var iconURL: String? + @Published public var requestId: String - public init() {} + public init() { + self.requestId = UUID().uuidString.lowercased() + } } public enum ShareExtensionStatus { @@ -126,7 +129,7 @@ struct CheckmarkButtonView: View { public struct ShareExtensionChildView: View { let viewModel: ShareExtensionChildViewModel let onAppearAction: () -> Void - let readNowButtonAction: () -> Void + let readNowButtonAction: (String) -> Void let dismissButtonTappedAction: (ReminderTime?, Bool) -> Void @State var reminderTime: ReminderTime? @@ -135,7 +138,7 @@ public struct ShareExtensionChildView: View { public init( viewModel: ShareExtensionChildViewModel, onAppearAction: @escaping () -> Void, - readNowButtonAction: @escaping () -> Void, + readNowButtonAction: @escaping (String) -> Void, dismissButtonTappedAction: @escaping (ReminderTime?, Bool) -> Void ) { self.viewModel = viewModel @@ -281,13 +284,12 @@ public struct ShareExtensionChildView: View { Spacer() HStack { - if FeatureFlag.enableReadNow { - Button( - action: { readNowButtonAction() }, - label: { Text("Read Now").frame(maxWidth: .infinity) } - ) - .buttonStyle(RoundedRectButtonStyle()) - } + Button( + action: { readNowButtonAction(self.viewModel.requestId) }, + label: { Text("Read Now").frame(maxWidth: .infinity) } + ) + .buttonStyle(RoundedRectButtonStyle()) + Button( action: { dismissButtonTappedAction(reminderTime, hideUntilReminded) From 8460ba1cea08894eeeb4c8c9cbd8650d3dbd35c4 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 7 Jun 2022 07:59:28 -0700 Subject: [PATCH 14/29] pop read now link when archiving or deleting from reader --- .../Sources/App/Views/WebReader/WebReaderContainer.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift index f984ad1d8..6031bd02f 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift @@ -89,6 +89,7 @@ import WebKit Button( action: { dataService.archiveLink(objectID: item.objectID, archived: !item.isArchived) + presentationMode.wrappedValue.dismiss() Snackbar.show(message: !item.isArchived ? "Link archived" : "Link moved to Inbox") }, label: { @@ -122,6 +123,7 @@ import WebKit Button("Remove Link", role: .destructive) { Snackbar.show(message: "Link removed") dataService.removeLink(objectID: item.objectID) + presentationMode.wrappedValue.dismiss() } Button("Cancel", role: .cancel, action: {}) } From ea0b689b5c1a50d2b030fbf58201dc2d482ab09c Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 7 Jun 2022 11:03:32 -0700 Subject: [PATCH 15/29] Remove unused var --- apple/OmnivoreKit/Sources/Models/PageScrapePayload.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/apple/OmnivoreKit/Sources/Models/PageScrapePayload.swift b/apple/OmnivoreKit/Sources/Models/PageScrapePayload.swift index ee4435cc2..1b997e82f 100644 --- a/apple/OmnivoreKit/Sources/Models/PageScrapePayload.swift +++ b/apple/OmnivoreKit/Sources/Models/PageScrapePayload.swift @@ -53,7 +53,6 @@ public enum PageScraper { var pageScrapePayload: PageScrapePayload? let PDFKey = UTType.pdf.identifier - let publicFileKey = UTType.fileURL.identifier let propertyListKey = UTType.propertyList.identifier let group = DispatchGroup() From b76b2ef4cb794ec653c9728c02c4fd17cfe116a5 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 7 Jun 2022 11:04:06 -0700 Subject: [PATCH 16/29] Better naming for sync methods --- .../Sources/Services/DataService/OfflineSync.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift index 8a9239957..faa00c72a 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift @@ -48,7 +48,7 @@ public extension DataService { } } - func syncPdf(id: String, localPdfURL: URL, url: String) async throws { + func createPageFromPdf(id: String, localPdfURL: URL, url: String) async throws { do { try await updateLinkedItemStatus(id: id, newId: nil, status: .isSyncing) @@ -72,7 +72,7 @@ public extension DataService { } } - func syncPage(id: String, originalHtml: String, title: String?, url: String) async throws -> String { + func createPage(id: String, originalHtml: String, title: String?, url: String) async throws -> String { do { try await updateLinkedItemStatus(id: id, newId: nil, status: .isSyncing) @@ -90,7 +90,7 @@ public extension DataService { } } - func syncUrl(id: String, url: String) async throws -> String { + func createPageFromUrl(id: String, url: String) async throws -> String { do { try await updateLinkedItemStatus(id: id, newId: nil, status: .isSyncing) @@ -117,7 +117,7 @@ public extension DataService { if let pdfUrlStr = localPdfURL, let localPdfURL = URL(string: pdfUrlStr) { Task { - try await syncPdf(id: id, localPdfURL: localPdfURL, url: url) + try await createPageFromPdf(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 @@ -131,9 +131,9 @@ public extension DataService { Task { if let originalHtml = originalHtml { - try await syncPage(id: id, originalHtml: originalHtml, title: title, url: url) + try await createPage(id: id, originalHtml: originalHtml, title: title, url: url) } else { - try await syncUrl(id: id, url: url) + try await createPageFromUrl(id: id, url: url) } } default: From 4ff8a9b83d1dbbccc6666d06b06e0ae54e2b2246 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 7 Jun 2022 11:05:53 -0700 Subject: [PATCH 17/29] When opening with a request id check core data first for an item --- .../Views/WebReader/WebReaderLoadingContainer.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderLoadingContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderLoadingContainer.swift index 046d0673a..756a9b57c 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderLoadingContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderLoadingContainer.swift @@ -1,3 +1,4 @@ +import CoreData import Models import Services import SwiftUI @@ -24,6 +25,16 @@ import Utils guard let username = username else { return } + let fetchRequest: NSFetchRequest = LinkedItem.fetchRequest() + fetchRequest.predicate = NSPredicate(format: "id == %@", requestID) + if let existingItem = try? dataService.viewContext.fetch(fetchRequest).first, + existingItem.serverSyncStatus == ServerSyncStatus.isNSync.rawValue + { + print("USING EXISTING ITEM", existingItem.serverSyncStatus) + item = existingItem + return + } + // If the page was locally created, make sure they are synced before we pull content await dataService.syncUnsyncedArticleContent(itemID: requestID) await fetchLinkedItem(dataService: dataService, requestID: requestID, username: username) From 611b237e1703cf365caf28019b11657dd99787bc Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 7 Jun 2022 12:00:22 -0700 Subject: [PATCH 18/29] Handle save operations completing in the background while opening itemIDs --- .../Share/ExtensionSaveService.swift | 6 +-- .../WebReader/WebReaderLoadingContainer.swift | 40 ++++++++++++++----- .../CoreDataModel.xcdatamodel/contents | 3 +- .../Services/DataService/DataService.swift | 1 + 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ExtensionSaveService.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ExtensionSaveService.swift index 1350d936a..f40b8a61c 100644 --- a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ExtensionSaveService.swift +++ b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ExtensionSaveService.swift @@ -168,11 +168,11 @@ class ExtensionSaveService { switch pageScrapePayload.contentType { case .none: - requestId = try await services.dataService.syncUrl(id: requestId, url: pageScrapePayload.url) + requestId = try await services.dataService.createPageFromUrl(id: requestId, url: pageScrapePayload.url) case let .pdf(localUrl): - try await services.dataService.syncPdf(id: requestId, localPdfURL: localUrl, url: pageScrapePayload.url) + try await services.dataService.createPageFromPdf(id: requestId, localPdfURL: localUrl, url: pageScrapePayload.url) case let .html(html, title, _): - requestId = try await services.dataService.syncPage( + requestId = try await services.dataService.createPage( id: requestId, originalHtml: html, title: title, diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderLoadingContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderLoadingContainer.swift index 756a9b57c..c09fc7816 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderLoadingContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderLoadingContainer.swift @@ -25,19 +25,15 @@ import Utils guard let username = username else { return } - let fetchRequest: NSFetchRequest = LinkedItem.fetchRequest() - fetchRequest.predicate = NSPredicate(format: "id == %@", requestID) - if let existingItem = try? dataService.viewContext.fetch(fetchRequest).first, - existingItem.serverSyncStatus == ServerSyncStatus.isNSync.rawValue - { - print("USING EXISTING ITEM", existingItem.serverSyncStatus) + let existing = existingItemOrItemId(dataService: dataService, requestID: requestID) + if let existingItem = existing.existingItem { item = existingItem return } // If the page was locally created, make sure they are synced before we pull content - await dataService.syncUnsyncedArticleContent(itemID: requestID) - await fetchLinkedItem(dataService: dataService, requestID: requestID, username: username) + await dataService.syncUnsyncedArticleContent(itemID: existing.itemID) + await fetchLinkedItem(dataService: dataService, requestID: existing.itemID, username: username) } private func fetchLinkedItem( @@ -64,9 +60,16 @@ import Utils do { let retryDelayInNanoSeconds = UInt64(requestCount * 2 * 1_000_000_000) try await Task.sleep(nanoseconds: retryDelayInNanoSeconds) + + let existing = existingItemOrItemId(dataService: dataService, requestID: requestID) + if let existingItem = existing.existingItem { + item = existingItem + return + } + await fetchLinkedItem( dataService: dataService, - requestID: requestID, + requestID: existing.itemID, username: username, requestCount: requestCount + 1 ) @@ -75,6 +78,25 @@ import Utils } } + private func existingItemOrItemId(dataService: DataService, requestID: String) -> (existingItem: LinkedItem?, itemID: String) { + let fetchRequest: NSFetchRequest = LinkedItem.fetchRequest() + fetchRequest.predicate = NSPredicate(format: "createdId == %@ OR id == %@", requestID, requestID) + if let existingItem = try? dataService.viewContext.fetch(fetchRequest).first { + // If the existing item is synced, we can use it + if let itemID = existingItem.id, existingItem.serverSyncStatus == ServerSyncStatus.isNSync.rawValue { + item = existingItem + return (existingItem: item, itemID: itemID) + } + + // If the existing item is not synced, we might have an updated request id + if let existingID = existingItem.id { + return (existingItem: nil, itemID: existingID) + } + } + + return (existingItem: nil, itemID: requestID) + } + func trackReadEvent() { guard let item = item else { return } diff --git a/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents b/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents index 63bdc0cd9..3316d15b9 100644 --- a/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents +++ b/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents @@ -24,6 +24,7 @@ + @@ -89,7 +90,7 @@ - + diff --git a/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift b/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift index 123faa838..7e12640b0 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift @@ -121,6 +121,7 @@ public final class DataService: ObservableObject { let existingItem = try? self.backgroundContext.fetch(fetchRequest).first let linkedItem = existingItem ?? LinkedItem(entity: LinkedItem.entity(), insertInto: self.backgroundContext) + linkedItem.createdId = requestId linkedItem.id = existingItem?.unwrappedID ?? requestId linkedItem.title = normalizedURL linkedItem.pageURLString = normalizedURL From d8e2285f2bd75189e0c66a016ef74f8ddebc1b37 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 7 Jun 2022 14:04:58 -0700 Subject: [PATCH 19/29] Make sure titles are updated when fetching updated article content --- .../Sources/App/Views/WebReader/WebReader.swift | 6 ++---- .../App/Views/WebReader/WebReaderContainer.swift | 3 +-- .../App/Views/WebReader/WebReaderContent.swift | 15 ++++++--------- .../Models/DataModels/ArticleContent.swift | 3 +++ .../DataService/Queries/ArticleContentQuery.swift | 2 ++ 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift index c24689f14..514d9c8f4 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift @@ -6,9 +6,8 @@ import WebKit #if os(iOS) struct WebReader: UIViewRepresentable { - let htmlContent: String - let highlightsJSONString: String let item: LinkedItem + let articleContent: ArticleContent let openLinkAction: (URL) -> Void let webViewActionHandler: (WKScriptMessage, WKScriptMessageReplyHandler?) -> Void let navBarVisibilityRatioUpdater: (Double) -> Void @@ -143,9 +142,8 @@ import WebKit webView.loadHTMLString( WebReaderContent( - htmlContent: htmlContent, - highlightsJSONString: highlightsJSONString, item: item, + articleContent: articleContent, isDark: UITraitCollection.current.userInterfaceStyle == .dark, fontSize: fontSize(), lineHeight: lineHeight(), diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift index 6031bd02f..4050d4cff 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift @@ -136,9 +136,8 @@ import WebKit ZStack { if let articleContent = viewModel.articleContent { WebReader( - htmlContent: articleContent.htmlContent, - highlightsJSONString: articleContent.highlightsJSONString, item: item, + articleContent: articleContent, openLinkAction: { #if os(macOS) NSWorkspace.shared.open($0) diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift index 87902f01a..df4bece5a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContent.swift @@ -7,16 +7,14 @@ struct WebReaderContent { let textFontSize: Int let lineHeight: Int let margin: Int - let htmlContent: String - let highlightsJSONString: String let item: LinkedItem let themeKey: String let fontFamily: WebFont + let articleContent: ArticleContent init( - htmlContent: String, - highlightsJSONString: String, item: LinkedItem, + articleContent: ArticleContent, isDark: Bool, fontSize: Int, lineHeight: Int, @@ -26,11 +24,10 @@ struct WebReaderContent { self.textFontSize = fontSize self.lineHeight = lineHeight self.margin = margin - self.htmlContent = htmlContent - self.highlightsJSONString = highlightsJSONString self.item = item self.themeKey = isDark ? "Gray" : "LightGray" self.fontFamily = fontFamily + self.articleContent = articleContent } // swiftlint:disable line_length @@ -52,7 +49,7 @@ struct WebReaderContent {