From 9c2509951dc1d94420857f0377f7fff99b101f8d Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 6 Jul 2022 15:10:59 -0700 Subject: [PATCH] consolidate share extension view code --- .../Share/ExtensionSaveService.swift | 183 ------------------ .../Share/ShareExtensionSaveOperation.swift | 76 ++++++++ .../Share/ShareExtensionViewModel.swift | 107 +++++++++- .../{ => Views}/ShareExtensionView.swift | 0 .../ShareExtensionViewComponents.swift | 0 .../SafariWebExtensionHandler.swift | 8 - 6 files changed, 182 insertions(+), 192 deletions(-) delete mode 100644 apple/OmnivoreKit/Sources/App/AppExtensions/Share/ExtensionSaveService.swift create mode 100644 apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionSaveOperation.swift rename apple/OmnivoreKit/Sources/App/AppExtensions/Share/{ => Views}/ShareExtensionView.swift (100%) rename apple/OmnivoreKit/Sources/App/AppExtensions/Share/{ => Views}/ShareExtensionViewComponents.swift (100%) diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ExtensionSaveService.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ExtensionSaveService.swift deleted file mode 100644 index c262936e3..000000000 --- a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ExtensionSaveService.swift +++ /dev/null @@ -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() - } -} diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionSaveOperation.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionSaveOperation.swift new file mode 100644 index 000000000..8af1f4715 --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionSaveOperation.swift @@ -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() + } +} diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionViewModel.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionViewModel.swift index d9a2b24eb..4d68a0add 100644 --- a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionViewModel.swift @@ -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 { diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionView.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift similarity index 100% rename from apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionView.swift rename to apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionViewComponents.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionViewComponents.swift similarity index 100% rename from apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionViewComponents.swift rename to apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionViewComponents.swift diff --git a/apple/Sources/SafariExtension/SafariWebExtensionHandler.swift b/apple/Sources/SafariExtension/SafariWebExtensionHandler.swift index 307197fe9..0ce9bb902 100644 --- a/apple/Sources/SafariExtension/SafariWebExtensionHandler.swift +++ b/apple/Sources/SafariExtension/SafariWebExtensionHandler.swift @@ -1,12 +1,4 @@ -// -// SafariWebExtensionHandler.swift -// Shared (Extension) -// -// Created by JacksonH on 10/8/21. -// - import App -import os.log import SafariServices import Services