diff --git a/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift b/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift index c7e33ab8b..5bf694f5b 100644 --- a/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift @@ -17,13 +17,28 @@ public final class PDFViewerViewModel: ObservableObject { self.feedItem = feedItem } - public func allHighlights() -> [Highlight] { + public func loadHighlights(completion onComplete: @escaping ([Highlight]) -> Void) { + guard let viewer = services.dataService.currentViewer else { return } + + services.dataService.pdfHighlightsPublisher(username: viewer.username, slug: feedItem.slug).sink( + receiveCompletion: { [weak self] completion in + guard case .failure = completion else { return } + onComplete(self?.allHighlights(fetchedHighlights: []) ?? []) + }, + receiveValue: { [weak self] highlights in + onComplete(self?.allHighlights(fetchedHighlights: highlights) ?? []) + } + ) + .store(in: &subscriptions) + } + + private func allHighlights(fetchedHighlights: [Highlight]) -> [Highlight] { var resultSet = [String: Highlight]() for highlight in services.dataService.cachedHighlights(pdfID: feedItem.id) { resultSet[highlight.id] = highlight } - for highlight in feedItem.highlights ?? [] { + for highlight in fetchedHighlights { resultSet[highlight.id] = highlight } for highlightId in services.dataService.fetchRemovedHighlightIds(pdfID: feedItem.id) { @@ -147,7 +162,6 @@ public final class PDFViewerViewModel: ObservableObject { } private func removeLocalHighlights(highlightIds: [String]) { - feedItem.highlights?.removeAll { highlightIds.contains($0.id) } services.dataService.removeHighlights(pdfID: feedItem.id, highlightIds: highlightIds) } } diff --git a/apple/OmnivoreKit/Sources/Models/FeedItem.swift b/apple/OmnivoreKit/Sources/Models/FeedItem.swift index b61022b01..bd64ec39b 100644 --- a/apple/OmnivoreKit/Sources/Models/FeedItem.swift +++ b/apple/OmnivoreKit/Sources/Models/FeedItem.swift @@ -27,7 +27,6 @@ public struct FeedItem: Identifiable, Hashable, Decodable { public let slug: String public let isArchived: Bool public let contentReader: String? - public var highlights: [Highlight]? public init( id: String, @@ -44,8 +43,7 @@ public struct FeedItem: Identifiable, Hashable, Decodable { publishDate: Date?, slug: String, isArchived: Bool, - contentReader: String?, - highlights: [Highlight]? + contentReader: String? ) { self.id = id self.title = title @@ -62,7 +60,6 @@ public struct FeedItem: Identifiable, Hashable, Decodable { self.slug = slug self.isArchived = isArchived self.contentReader = contentReader - self.highlights = highlights } enum CodingKeys: String, CodingKey { diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/LibraryItemsQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/LibraryItemsQuery.swift index 185865a3c..d78408487 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Queries/LibraryItemsQuery.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/LibraryItemsQuery.swift @@ -125,18 +125,6 @@ public extension DataService { } } -let highlightSelection = Selection.Highlight { - Highlight( - id: try $0.id(), - shortId: try $0.shortId(), - quote: try $0.quote(), - prefix: try $0.prefix(), - suffix: try $0.suffix(), - patch: try $0.patch(), - annotation: try $0.annotation() - ) -} - let homeFeedItemSelection = Selection.Article { FeedItem( id: try $0.id(), @@ -153,8 +141,7 @@ let homeFeedItemSelection = Selection.Article { publishDate: try $0.publishedAt()?.value, slug: try $0.slug(), isArchived: try $0.isArchived(), - contentReader: try $0.contentReader().rawValue, - highlights: try $0.highlights(selection: highlightSelection.list) + contentReader: try $0.contentReader().rawValue ) } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/PDFHighlightsQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/PDFHighlightsQuery.swift new file mode 100644 index 000000000..6b0c0d215 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/PDFHighlightsQuery.swift @@ -0,0 +1,67 @@ +import Combine +import Foundation +import Models +import SwiftGraphQL + +public extension DataService { + func pdfHighlightsPublisher(username: String, slug: String) -> AnyPublisher<[Highlight], ServerError> { + enum QueryResult { + case success(result: [Highlight]) + case error(error: String) + } + + let highlightSelection = Selection.Highlight { + Highlight( + id: try $0.id(), + shortId: try $0.shortId(), + quote: try $0.quote(), + prefix: try $0.prefix(), + suffix: try $0.suffix(), + patch: try $0.patch(), + annotation: try $0.annotation() + ) + } + + let articleSelection = Selection.Article { + try $0.highlights(selection: highlightSelection.list) + } + + let selection = Selection { + try $0.on( + articleSuccess: .init { + QueryResult.success(result: try $0.article(selection: articleSelection)) + }, + articleError: .init { + QueryResult.error(error: try $0.errorCodes().description) + } + ) + } + + let query = Selection.Query { + try $0.article(username: username, slug: slug, selection: selection) + } + + let path = appEnvironment.graphqlPath + let headers = networker.defaultHeaders + + return Deferred { + Future { promise in + send(query, to: path, headers: headers) { result in + switch result { + case let .success(payload): + switch payload.data { + case let .success(result: result): + promise(.success(result)) + case .error: + promise(.failure(.unknown)) + } + case .failure: + promise(.failure(.unknown)) + } + } + } + } + .receive(on: DispatchQueue.main) + .eraseToAnyPublisher() + } +} diff --git a/apple/Sources/PDFViewer.swift b/apple/Sources/PDFViewer.swift index 0dc6de4b4..993f7436b 100644 --- a/apple/Sources/PDFViewer.swift +++ b/apple/Sources/PDFViewer.swift @@ -291,29 +291,30 @@ import Utils } override func didCreateDocumentProvider(_ documentProvider: PDFDocumentProvider) -> PDFDocumentProvider { - DispatchQueue.main.async { [self] in - if self.highlightsApplied { - return + if !highlightsApplied { + DispatchQueue.main.async { [self] in + self.applyHighlights(documentProvider: documentProvider) } - - var annnotations: [Annotation] = [] - for highlight in self.viewModel.allHighlights() { - let data = highlight.patch.data(using: String.Encoding.utf8) - if let data = data { - guard let annotation = try? Annotation(fromInstantJSON: data, documentProvider: documentProvider) else { - continue - } - annnotations.append(annotation) - } - } - add(annotations: annnotations) - - highlightsApplied = true } return documentProvider } + private func applyHighlights(documentProvider: PDFDocumentProvider) { + viewModel.loadHighlights { [weak self] highlights in + var annnotations: [Annotation] = [] + for highlight in highlights { + guard let data = highlight.patch.data(using: String.Encoding.utf8) else { continue } + let annotation = try? Annotation(fromInstantJSON: data, documentProvider: documentProvider) + guard let annotation = annotation else { continue } + annnotations.append(annotation) + } + self?.add(annotations: annnotations) + + self?.highlightsApplied = true + } + } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented")