diff --git a/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift b/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift index 4240b9509..d9b3c5e48 100644 --- a/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift @@ -41,7 +41,7 @@ public final class PDFViewerViewModel: ObservableObject { for highlight in fetchedHighlights { resultSet[highlight.id] = highlight } - for highlightId in services.dataService.fetchRemovedHighlightIds(pdfID: feedItem.id) { + for highlightId in services.dataService.deletedHighlightsIDs { resultSet.removeValue(forKey: highlightId) } return Array(resultSet.values) @@ -164,6 +164,6 @@ public final class PDFViewerViewModel: ObservableObject { } private func removeLocalHighlights(highlightIds: [String]) { - services.dataService.removeHighlights(pdfID: feedItem.id, highlightIds: highlightIds) + services.dataService.removeHighlights(highlightIds: highlightIds) } } 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 8002ea47e..449b5cd29 100644 --- a/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents +++ b/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents @@ -3,6 +3,11 @@ + + + + + @@ -22,6 +27,11 @@ + + + + + @@ -29,23 +39,35 @@ + + + + + + + + + + + + - + \ No newline at end of file diff --git a/apple/OmnivoreKit/Sources/Models/Highlight.swift b/apple/OmnivoreKit/Sources/Models/Highlight.swift index 477c33352..a7d079125 100644 --- a/apple/OmnivoreKit/Sources/Models/Highlight.swift +++ b/apple/OmnivoreKit/Sources/Models/Highlight.swift @@ -1,3 +1,4 @@ +import CoreData import Foundation public struct Highlight: Identifiable, Hashable, Codable { @@ -35,4 +36,36 @@ public struct Highlight: Identifiable, Hashable, Codable { self.updatedAt = updatedAt self.createdByMe = createdByMe } + + public func toManagedObject(context: NSManagedObjectContext, associatedItemID: String) -> PersistedHighlight { + let persistedHighlight = PersistedHighlight(context: context) + persistedHighlight.associatedItemId = associatedItemID + persistedHighlight.markedForDeletion = false + persistedHighlight.id = id + persistedHighlight.shortId = shortId + persistedHighlight.quote = quote + persistedHighlight.prefix = prefix + persistedHighlight.suffix = suffix + persistedHighlight.patch = patch + persistedHighlight.annotation = annotation + persistedHighlight.createdAt = createdAt + persistedHighlight.updatedAt = updatedAt + persistedHighlight.createdByMe = createdByMe + return persistedHighlight + } + + public static func make(from persistedHighlight: PersistedHighlight) -> Highlight { + Highlight( + id: persistedHighlight.id ?? "", + shortId: persistedHighlight.shortId ?? "", + quote: persistedHighlight.quote ?? "", + prefix: persistedHighlight.prefix, + suffix: persistedHighlight.suffix, + patch: persistedHighlight.patch ?? "", + annotation: persistedHighlight.annotation, + createdByMe: persistedHighlight.createdByMe, + createdAt: persistedHighlight.createdAt, + updatedAt: persistedHighlight.updatedAt + ) + } } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift b/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift index b0f4e7e41..83fa20999 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift @@ -11,11 +11,9 @@ public final class DataService: ObservableObject { public internal(set) var currentViewer: Viewer? let networker: Networker - let highlightsCache = NSCache() - let highlightsCacheQueue = DispatchQueue(label: "app.omnivore.highlights.cache.queue", attributes: .concurrent) - let persistentContainer: PersistentContainer var subscriptions = Set() + public var deletedHighlightsIDs = Set() public init(appEnvironment: AppEnvironment, networker: Networker) { self.appEnvironment = appEnvironment @@ -30,7 +28,21 @@ public final class DataService: ObservableObject { } public func clearHighlights() { - highlightsCache.removeAllObjects() + deletedHighlightsIDs.removeAll() + + let fetchRequest: NSFetchRequest = PersistedHighlight.fetchRequest() + + let highlights = (try? persistentContainer.viewContext.fetch(fetchRequest)) ?? [] + + for highlight in highlights { + persistentContainer.viewContext.delete(highlight) + } + + do { + try persistentContainer.viewContext.save() + } catch { + print("failed to delete objects") + } } public func switchAppEnvironment(appEnvironment: AppEnvironment) { diff --git a/apple/OmnivoreKit/Sources/Services/Persistence/PersistableModels/CachedPDFHighlights.swift b/apple/OmnivoreKit/Sources/Services/Persistence/PersistableModels/CachedPDFHighlights.swift index 35ece139b..c9a225c11 100644 --- a/apple/OmnivoreKit/Sources/Services/Persistence/PersistableModels/CachedPDFHighlights.swift +++ b/apple/OmnivoreKit/Sources/Services/Persistence/PersistableModels/CachedPDFHighlights.swift @@ -1,57 +1,53 @@ import Combine +import CoreData import Foundation import Models -final class CachedPDFHighlights { - init(pdfID: String, highlights: [Highlight], removedHighlightIDs: [String]) { - self.pdfID = pdfID - self.highlights = highlights - self.removedHighlightIDs = removedHighlightIDs - } - - let pdfID: String - var highlights: [Highlight] - var removedHighlightIDs: [String] -} - public extension DataService { func cachedHighlights(pdfID: String) -> [Highlight] { - fetchCachedHighlights(pdfID: pdfID as NSString)?.highlights ?? [] - } + let fetchRequest: NSFetchRequest = PersistedHighlight.fetchRequest() + fetchRequest.predicate = NSPredicate( + format: "associatedItemId = %@ AND markedForDeletion = %@", pdfID, false + ) - func fetchRemovedHighlightIds(pdfID: String) -> [String] { - fetchCachedHighlights(pdfID: pdfID as NSString)?.removedHighlightIDs ?? [] + let highlights = (try? persistentContainer.viewContext.fetch(fetchRequest)) ?? [] + return highlights.map { Highlight.make(from: $0) } } func persistHighlight(pdfID: String, highlight: Highlight) { - let cachedHighlights = - fetchCachedHighlights(pdfID: pdfID as NSString) - ?? CachedPDFHighlights(pdfID: pdfID, highlights: [], removedHighlightIDs: []) + _ = highlight.toManagedObject( + context: persistentContainer.viewContext, + associatedItemID: pdfID + ) - cachedHighlights.highlights.append(highlight) - insertCachedHighlights(highlights: cachedHighlights, pdfID: pdfID as NSString) - } - - func removeHighlights(pdfID: String, highlightIds: [String]) { - let cachedHighlights = - fetchCachedHighlights(pdfID: pdfID as NSString) - ?? CachedPDFHighlights(pdfID: pdfID, highlights: [], removedHighlightIDs: []) - - cachedHighlights.removedHighlightIDs.append(contentsOf: highlightIds) - insertCachedHighlights(highlights: cachedHighlights, pdfID: pdfID as NSString) - } - - private func fetchCachedHighlights(pdfID: NSString) -> CachedPDFHighlights? { - var cachedHighlights: CachedPDFHighlights? - highlightsCacheQueue.sync { - cachedHighlights = highlightsCache.object(forKey: pdfID as NSString) + do { + try persistentContainer.viewContext.save() + print("PersistedHighlight saved succesfully") + } catch { + persistentContainer.viewContext.rollback() + print("Failed to save PersistedHighlight: \(error)") } - return cachedHighlights } - private func insertCachedHighlights(highlights: CachedPDFHighlights, pdfID: NSString) { - highlightsCacheQueue.async(flags: .barrier) { - self.highlightsCache.setObject(highlights, forKey: pdfID as AnyObject) + func removeHighlights(highlightIds: [String]) { + for highlightID in highlightIds { + deletedHighlightsIDs.insert(highlightID) + } + + let fetchRequest: NSFetchRequest = PersistedHighlight.fetchRequest() + fetchRequest.predicate = NSPredicate(format: "id IN %@", highlightIds) + guard let highlights = try? persistentContainer.viewContext.fetch(fetchRequest) else { return } + + for highlight in highlights { + highlight.markedForDeletion = true + } + + do { + try persistentContainer.viewContext.save() + print("PersistedHighlight(s) updated succesfully") + } catch { + persistentContainer.viewContext.rollback() + print("Failed to update PersistedHighlight(s): \(error)") } } }