Merge pull request #238 from omnivore-app/faeture/highlights-loading
PDF Highlights Loading
This commit is contained in:
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
@ -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")
|
||||
|
||||
Reference in New Issue
Block a user