consolidate share extension view code

This commit is contained in:
Satindar Dhillon
2022-07-06 15:10:59 -07:00
parent 16358bf230
commit 9c2509951d
6 changed files with 182 additions and 192 deletions

View File

@ -1,183 +0,0 @@
import Foundation
import Models
import Services
import Utils
import Views
extension ShareExtensionViewModel {
public func save(_ extensionContext: NSExtensionContext) {
PageScraper.scrape(extensionContext: extensionContext) { [weak self] result in
guard let self = self else { return }
switch result {
case let .success(payload):
DispatchQueue.main.async {
self.status = .saved
let url = URLComponents(string: payload.url)
let hostname = URL(string: payload.url)?.host ?? ""
switch payload.contentType {
case let .html(html: _, title: title, iconURL: iconURL):
self.title = title
self.iconURL = iconURL
self.url = hostname
case .none:
self.url = hostname
self.title = payload.url
if var url = url {
url.path = "/favicon.ico"
self.iconURL = url.url?.absoluteString
}
case let .pdf(localUrl: localUrl):
self.url = hostname
self.title = PDFUtils.titleFromPdfFile(localUrl.absoluteString)
Task {
let localThumbnail = try await PDFUtils.createThumbnailFor(inputUrl: localUrl)
DispatchQueue.main.async {
self.iconURL = localThumbnail?.absoluteString
}
}
}
}
#if os(iOS)
self.queueSaveOperation(payload)
#else
Task {
await createPage(services: self.services, pageScrapePayload: payload)
}
#endif
case .failure:
DispatchQueue.main.async {
self.status = .failed(error: .unknown(description: "Could not retrieve content"))
}
}
}
}
func createPage(pageScrapePayload: PageScrapePayload) async -> Bool {
var newRequestID: String?
do {
try await services.dataService.persistPageScrapePayload(pageScrapePayload, requestId: requestId)
} catch {
updateStatusOnMain(
requestId: nil,
newStatus: .failed(error: SaveArticleError.unknown(description: "Unable to access content"))
)
return false
}
do {
updateStatusOnMain(requestId: requestId, newStatus: .saved)
switch pageScrapePayload.contentType {
case .none:
newRequestID = try await services.dataService.createPageFromUrl(id: requestId, url: pageScrapePayload.url)
case let .pdf(localUrl):
try await services.dataService.createPageFromPdf(
id: requestId,
localPdfURL: localUrl,
url: pageScrapePayload.url
)
case let .html(html, title, _):
newRequestID = try await services.dataService.createPage(
id: requestId,
originalHtml: html,
title: title,
url: pageScrapePayload.url
)
}
} catch {
updateStatusOnMain(
requestId: nil,
newStatus: .syncFailed(error: SaveArticleError.unknown(description: "Unknown Error"))
)
return false
}
updateStatusOnMain(requestId: newRequestID, newStatus: .synced)
return true
}
func updateStatusOnMain(requestId: String?, newStatus: ShareExtensionStatus) {
DispatchQueue.main.async {
self.status = newStatus
if let requestId = requestId {
self.requestId = requestId
}
}
}
}
final class SaveOperation: Operation, URLSessionDelegate {
let pageScrapePayload: PageScrapePayload
let shareExtensionViewModel: ShareExtensionViewModel
var queue: OperationQueue?
var uploadTask: URLSessionTask?
enum State: Int {
case created
case started
case finished
}
init(pageScrapePayload: PageScrapePayload, shareExtensionViewModel: ShareExtensionViewModel) {
self.pageScrapePayload = pageScrapePayload
self.shareExtensionViewModel = shareExtensionViewModel
self.state = .created
}
public var state: State = .created {
willSet {
willChangeValue(forKey: "isReady")
willChangeValue(forKey: "isExecuting")
willChangeValue(forKey: "isFinished")
willChangeValue(forKey: "isCancelled")
}
didSet {
didChangeValue(forKey: "isCancelled")
didChangeValue(forKey: "isFinished")
didChangeValue(forKey: "isExecuting")
didChangeValue(forKey: "isReady")
}
}
override var isAsynchronous: Bool {
true
}
override var isReady: Bool {
true
}
override var isExecuting: Bool {
self.state == .started
}
override var isFinished: Bool {
self.state == .finished
}
override func start() {
guard !isCancelled else { return }
state = .started
queue = OperationQueue()
Task {
let pageCreated = await shareExtensionViewModel.createPage(
pageScrapePayload: pageScrapePayload
)
if pageCreated {
state = .finished
}
}
}
override func cancel() {
super.cancel()
}
}

View File

@ -0,0 +1,76 @@
import Foundation
import Models
import Services
import Utils
import Views
final class ShareExtensionSaveOperation: Operation, URLSessionDelegate {
let pageScrapePayload: PageScrapePayload
let shareExtensionViewModel: ShareExtensionViewModel
var queue: OperationQueue?
var uploadTask: URLSessionTask?
enum State: Int {
case created
case started
case finished
}
init(pageScrapePayload: PageScrapePayload, shareExtensionViewModel: ShareExtensionViewModel) {
self.pageScrapePayload = pageScrapePayload
self.shareExtensionViewModel = shareExtensionViewModel
self.state = .created
}
public var state: State = .created {
willSet {
willChangeValue(forKey: "isReady")
willChangeValue(forKey: "isExecuting")
willChangeValue(forKey: "isFinished")
willChangeValue(forKey: "isCancelled")
}
didSet {
didChangeValue(forKey: "isCancelled")
didChangeValue(forKey: "isFinished")
didChangeValue(forKey: "isExecuting")
didChangeValue(forKey: "isReady")
}
}
override var isAsynchronous: Bool {
true
}
override var isReady: Bool {
true
}
override var isExecuting: Bool {
self.state == .started
}
override var isFinished: Bool {
self.state == .finished
}
override func start() {
guard !isCancelled else { return }
state = .started
queue = OperationQueue()
Task {
let pageCreated = await shareExtensionViewModel.createPage(
pageScrapePayload: pageScrapePayload
)
if pageCreated {
state = .finished
}
}
}
override func cancel() {
super.cancel()
}
}

View File

@ -43,12 +43,117 @@ public class ShareExtensionViewModel: ObservableObject {
return
}
let operation = SaveOperation(pageScrapePayload: payload, shareExtensionViewModel: self)
let operation = ShareExtensionSaveOperation(pageScrapePayload: payload, shareExtensionViewModel: self)
self.queue.addOperation(operation)
self.queue.waitUntilAllOperationsAreFinished()
}
}
#endif
public func save(_ extensionContext: NSExtensionContext) {
PageScraper.scrape(extensionContext: extensionContext) { [weak self] result in
guard let self = self else { return }
switch result {
case let .success(payload):
DispatchQueue.main.async {
self.status = .saved
let url = URLComponents(string: payload.url)
let hostname = URL(string: payload.url)?.host ?? ""
switch payload.contentType {
case let .html(html: _, title: title, iconURL: iconURL):
self.title = title
self.iconURL = iconURL
self.url = hostname
case .none:
self.url = hostname
self.title = payload.url
if var url = url {
url.path = "/favicon.ico"
self.iconURL = url.url?.absoluteString
}
case let .pdf(localUrl: localUrl):
self.url = hostname
self.title = PDFUtils.titleFromPdfFile(localUrl.absoluteString)
Task {
let localThumbnail = try await PDFUtils.createThumbnailFor(inputUrl: localUrl)
DispatchQueue.main.async {
self.iconURL = localThumbnail?.absoluteString
}
}
}
}
#if os(iOS)
self.queueSaveOperation(payload)
#else
Task {
await createPage(services: self.services, pageScrapePayload: payload)
}
#endif
case .failure:
DispatchQueue.main.async {
self.status = .failed(error: .unknown(description: "Could not retrieve content"))
}
}
}
}
func createPage(pageScrapePayload: PageScrapePayload) async -> Bool {
var newRequestID: String?
do {
try await services.dataService.persistPageScrapePayload(pageScrapePayload, requestId: requestId)
} catch {
updateStatusOnMain(
requestId: nil,
newStatus: .failed(error: SaveArticleError.unknown(description: "Unable to access content"))
)
return false
}
do {
updateStatusOnMain(requestId: requestId, newStatus: .saved)
switch pageScrapePayload.contentType {
case .none:
newRequestID = try await services.dataService.createPageFromUrl(id: requestId, url: pageScrapePayload.url)
case let .pdf(localUrl):
try await services.dataService.createPageFromPdf(
id: requestId,
localPdfURL: localUrl,
url: pageScrapePayload.url
)
case let .html(html, title, _):
newRequestID = try await services.dataService.createPage(
id: requestId,
originalHtml: html,
title: title,
url: pageScrapePayload.url
)
}
} catch {
updateStatusOnMain(
requestId: nil,
newStatus: .syncFailed(error: SaveArticleError.unknown(description: "Unknown Error"))
)
return false
}
updateStatusOnMain(requestId: newRequestID, newStatus: .synced)
return true
}
func updateStatusOnMain(requestId: String?, newStatus: ShareExtensionStatus) {
DispatchQueue.main.async {
self.status = newStatus
if let requestId = requestId {
self.requestId = requestId
}
}
}
}
public enum ShareExtensionStatus {

View File

@ -1,12 +1,4 @@
//
// SafariWebExtensionHandler.swift
// Shared (Extension)
//
// Created by JacksonH on 10/8/21.
//
import App
import os.log
import SafariServices
import Services