Merge pull request #238 from omnivore-app/faeture/highlights-loading

PDF Highlights Loading
This commit is contained in:
Satindar Dhillon
2022-03-15 14:38:50 -07:00
committed by GitHub
5 changed files with 104 additions and 38 deletions

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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
)
}

View File

@ -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<QueryResult, Unions.ArticleResult> {
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()
}
}

View File

@ -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")