diff --git a/apple/Omnivore.xcodeproj/project.pbxproj b/apple/Omnivore.xcodeproj/project.pbxproj index 223e4eabc..bd10aa72a 100644 --- a/apple/Omnivore.xcodeproj/project.pbxproj +++ b/apple/Omnivore.xcodeproj/project.pbxproj @@ -10,9 +10,6 @@ 0411792A26A22860004AE24F /* MacAppSmokeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0411792926A22860004AE24F /* MacAppSmokeTest.swift */; }; 0418837E2742E99F003E0001 /* Intercom in Frameworks */ = {isa = PBXBuildFile; productRef = 0418837D2742E99F003E0001 /* Intercom */; }; 041883802742FCF2003E0001 /* Utils in Frameworks */ = {isa = PBXBuildFile; productRef = 0418837F2742FCF2003E0001 /* Utils */; }; - 042184ED273AD426002357B0 /* PSPDFKit in Frameworks */ = {isa = PBXBuildFile; productRef = 042184EC273AD426002357B0 /* PSPDFKit */; }; - 042184EF273AD5F3002357B0 /* PDFViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 042184EE273AD5F3002357B0 /* PDFViewer.swift */; }; - 042184F0273AD5F3002357B0 /* PDFViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 042184EE273AD5F3002357B0 /* PDFViewer.swift */; }; 042F48DC26DFD10E00BF98FC /* iOSLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 042F48DB26DFD10E00BF98FC /* iOSLaunchTests.swift */; }; 045B1681279147E7005047F7 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 045B1680279147E7005047F7 /* FirebaseMessaging */; }; 0465B9BE26CDD35F005558CD /* MainApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81BE98F0CB588F5FC577A13 /* MainApp.swift */; }; @@ -44,10 +41,6 @@ 42321E882714E6B00056429F /* styles in Resources */ = {isa = PBXBuildFile; fileRef = 42321E832714E6B00056429F /* styles */; }; 42321E892714E6B00056429F /* views in Resources */ = {isa = PBXBuildFile; fileRef = 42321E842714E6B00056429F /* views */; }; 42321E8A2714E6B00056429F /* views in Resources */ = {isa = PBXBuildFile; fileRef = 42321E842714E6B00056429F /* views */; }; - 4255C6D82755A5350006422A /* NanoID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4255C6D72755A5350006422A /* NanoID.swift */; }; - 4255C6D92755A5350006422A /* NanoID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4255C6D72755A5350006422A /* NanoID.swift */; }; - 426408732744676C00A2AE46 /* PDFReaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 426408722744676B00A2AE46 /* PDFReaderView.swift */; }; - 426408742744676C00A2AE46 /* PDFReaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 426408722744676B00A2AE46 /* PDFReaderView.swift */; }; 42FF1B33271154A700B38C38 /* SafariWebExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42FF1AEB271154A600B38C38 /* SafariWebExtensionHandler.swift */; }; 42FF1B34271154A700B38C38 /* SafariWebExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42FF1AEB271154A600B38C38 /* SafariWebExtensionHandler.swift */; }; 42FF1B35271154A700B38C38 /* _locales in Resources */ = {isa = PBXBuildFile; fileRef = 42FF1AED271154A600B38C38 /* _locales */; }; @@ -184,7 +177,6 @@ 0411792726A22860004AE24F /* UnitTests-MacApp.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UnitTests-MacApp.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 0411792926A22860004AE24F /* MacAppSmokeTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAppSmokeTest.swift; sourceTree = ""; }; 0411792B26A22860004AE24F /* MacUnitTests.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = MacUnitTests.plist; sourceTree = ""; }; - 042184EE273AD5F3002357B0 /* PDFViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PDFViewer.swift; sourceTree = ""; }; 042F48D926DFD10E00BF98FC /* UITests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UITests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 042F48DB26DFD10E00BF98FC /* iOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSLaunchTests.swift; sourceTree = ""; }; 042F48DD26DFD10E00BF98FC /* iOSUITests.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = iOSUITests.plist; sourceTree = ""; }; @@ -215,8 +207,6 @@ 42321E822714E6B00056429F /* scripts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = scripts; sourceTree = ""; }; 42321E832714E6B00056429F /* styles */ = {isa = PBXFileReference; lastKnownFileType = folder; path = styles; sourceTree = ""; }; 42321E842714E6B00056429F /* views */ = {isa = PBXFileReference; lastKnownFileType = folder; path = views; sourceTree = ""; }; - 4255C6D72755A5350006422A /* NanoID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NanoID.swift; sourceTree = ""; }; - 426408722744676B00A2AE46 /* PDFReaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PDFReaderView.swift; sourceTree = ""; }; 42FF1AEB271154A600B38C38 /* SafariWebExtensionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariWebExtensionHandler.swift; sourceTree = ""; }; 42FF1AED271154A600B38C38 /* _locales */ = {isa = PBXFileReference; lastKnownFileType = folder; path = _locales; sourceTree = ""; }; 42FF1AEE271154A600B38C38 /* images */ = {isa = PBXFileReference; lastKnownFileType = folder; path = images; sourceTree = ""; }; @@ -315,7 +305,6 @@ 0418837E2742E99F003E0001 /* Intercom in Frameworks */, 045B1681279147E7005047F7 /* FirebaseMessaging in Frameworks */, 041883802742FCF2003E0001 /* Utils in Frameworks */, - 042184ED273AD426002357B0 /* PSPDFKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -478,9 +467,6 @@ children = ( 0480E71B26D95096006CAE2F /* AppDelegate.swift */, D81BE98F0CB588F5FC577A13 /* MainApp.swift */, - 042184EE273AD5F3002357B0 /* PDFViewer.swift */, - 426408722744676B00A2AE46 /* PDFReaderView.swift */, - 4255C6D72755A5350006422A /* NanoID.swift */, 42FF1AEA271154A600B38C38 /* SafariExtension */, B330B55BAF36E624637EE3BE /* ShareExtension */, 04920CC5279671EF003EC1B6 /* PushNotificationConfig.swift */, @@ -719,7 +705,6 @@ name = "Omnivore-iOS"; packageProductDependencies = ( 8F13016D33DF1598D7157563 /* App */, - 042184EC273AD426002357B0 /* PSPDFKit */, 0418837D2742E99F003E0001 /* Intercom */, 0418837F2742FCF2003E0001 /* Utils */, 045B1680279147E7005047F7 /* FirebaseMessaging */, @@ -796,7 +781,6 @@ ); mainGroup = 2EE5B57B588CF4ADBA39A106; packageReferences = ( - 042184EB273AD426002357B0 /* XCRemoteSwiftPackageReference "PSPDFKit-SP" */, 0418837C2742E99F003E0001 /* XCRemoteSwiftPackageReference "intercom-ios" */, 048F592A2790EAF800E0B494 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, ); @@ -1039,10 +1023,7 @@ buildActionMask = 2147483647; files = ( 0480E71D26D95096006CAE2F /* AppDelegate.swift in Sources */, - 4255C6D92755A5350006422A /* NanoID.swift in Sources */, 0465B9BE26CDD35F005558CD /* MainApp.swift in Sources */, - 426408742744676C00A2AE46 /* PDFReaderView.swift in Sources */, - 042184F0273AD5F3002357B0 /* PDFViewer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1075,11 +1056,8 @@ buildActionMask = 2147483647; files = ( 0480E71C26D95096006CAE2F /* AppDelegate.swift in Sources */, - 4255C6D82755A5350006422A /* NanoID.swift in Sources */, 04920CC6279671EF003EC1B6 /* PushNotificationConfig.swift in Sources */, CA7EE773095F267516D7AC98 /* MainApp.swift in Sources */, - 426408732744676C00A2AE46 /* PDFReaderView.swift in Sources */, - 042184EF273AD5F3002357B0 /* PDFViewer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1463,7 +1441,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.7.0; + MARKETING_VERSION = 1.7.1; PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app; PRODUCT_NAME = Omnivore; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1495,7 +1473,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.7.0; + MARKETING_VERSION = 1.7.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( @@ -1534,7 +1512,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.7.0; + MARKETING_VERSION = 1.7.1; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( "-framework", @@ -1696,7 +1674,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.7.0; + MARKETING_VERSION = 1.7.1; PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.share-extension"; PRODUCT_NAME = ShareExtension; SDKROOT = iphoneos; @@ -1750,7 +1728,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.7.0; + MARKETING_VERSION = 1.7.1; PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app; PRODUCT_NAME = Omnivore; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1778,7 +1756,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.7.0; + MARKETING_VERSION = 1.7.1; PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.share-extension"; PRODUCT_NAME = ShareExtension; SDKROOT = iphoneos; @@ -1964,14 +1942,6 @@ version = 11.1.2; }; }; - 042184EB273AD426002357B0 /* XCRemoteSwiftPackageReference "PSPDFKit-SP" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/PSPDFKit/PSPDFKit-SP"; - requirement = { - branch = master; - kind = branch; - }; - }; 048F592A2790EAF800E0B494 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; @@ -1992,11 +1962,6 @@ isa = XCSwiftPackageProductDependency; productName = Utils; }; - 042184EC273AD426002357B0 /* PSPDFKit */ = { - isa = XCSwiftPackageProductDependency; - package = 042184EB273AD426002357B0 /* XCRemoteSwiftPackageReference "PSPDFKit-SP" */; - productName = PSPDFKit; - }; 045B1680279147E7005047F7 /* FirebaseMessaging */ = { isa = XCSwiftPackageProductDependency; package = 048F592A2790EAF800E0B494 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; diff --git a/apple/Omnivore.xcworkspace/xcshareddata/swiftpm/Package.resolved b/apple/Omnivore.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4f826f753..fa6f4cbb3 100644 --- a/apple/Omnivore.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/apple/Omnivore.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -141,7 +141,7 @@ "location" : "https://github.com/PSPDFKit/PSPDFKit-SP", "state" : { "branch" : "master", - "revision" : "344c895fea62eb42c6b56e6a060d5dff73cc5bc2" + "revision" : "0e18629c443e3f39ecfee0f600d9ef5551ecf488" } }, { diff --git a/apple/OmnivoreKit/Package.swift b/apple/OmnivoreKit/Package.swift index 1897ffaa7..6fafc0cb0 100644 --- a/apple/OmnivoreKit/Package.swift +++ b/apple/OmnivoreKit/Package.swift @@ -16,15 +16,9 @@ let package = Package( .library(name: "Models", targets: ["Models"]), .library(name: "Utils", targets: ["Utils"]) ], - dependencies: [ - .package(url: "https://github.com/openid/AppAuth-iOS.git", .upToNextMajor(from: "1.4.0")), - .package(url: "https://github.com/Square/Valet", from: "4.1.2"), - .package(url: "https://github.com/maticzav/swift-graphql", from: "2.3.1"), - .package(url: "https://github.com/siteline/SwiftUI-Introspect.git", from: "0.1.4"), - .package(url: "git@github.com:segmentio/analytics-swift.git", .upToNextMajor(from: "1.0.0")) - ], + dependencies: dependencies, targets: [ - .target(name: "App", dependencies: ["Views", "Services", "Models", "Utils"]), + .target(name: "App", dependencies: appPackageDependencies), .testTarget(name: "AppTests", dependencies: ["App"]), .target( name: "Views", @@ -58,3 +52,25 @@ let package = Package( .testTarget(name: "UtilsTests", dependencies: ["Utils"]) ] ) + +var appPackageDependencies: [Target.Dependency] { + var deps: [Target.Dependency] = ["Views", "Services", "Models", "Utils"] +// #if canImport(UIKit) + deps.append(.product(name: "PSPDFKit", package: "PSPDFKit-SP")) +// #endif + return deps +} + +var dependencies: [Package.Dependency] { + var deps: [Package.Dependency] = [ + .package(url: "https://github.com/openid/AppAuth-iOS.git", .upToNextMajor(from: "1.4.0")), + .package(url: "https://github.com/Square/Valet", from: "4.1.2"), + .package(url: "https://github.com/maticzav/swift-graphql", from: "2.3.1"), + .package(url: "https://github.com/siteline/SwiftUI-Introspect.git", from: "0.1.4"), + .package(url: "git@github.com:segmentio/analytics-swift.git", .upToNextMajor(from: "1.0.0")) + ] +// #if canImport(UIKit) + deps.append(.package(url: "https://github.com/PSPDFKit/PSPDFKit-SP", branch: "master")) +// #endif + return deps +} diff --git a/apple/Sources/NanoID.swift b/apple/OmnivoreKit/Sources/App/PDFSupport/NanoID.swift similarity index 100% rename from apple/Sources/NanoID.swift rename to apple/OmnivoreKit/Sources/App/PDFSupport/NanoID.swift diff --git a/apple/Sources/PDFReaderView.swift b/apple/OmnivoreKit/Sources/App/PDFSupport/PDFReaderView.swift similarity index 87% rename from apple/Sources/PDFReaderView.swift rename to apple/OmnivoreKit/Sources/App/PDFSupport/PDFReaderView.swift index 59131fb55..66309f623 100644 --- a/apple/Sources/PDFReaderView.swift +++ b/apple/OmnivoreKit/Sources/App/PDFSupport/PDFReaderView.swift @@ -1,13 +1,18 @@ -import Models -import SwiftUI -import Utils -import WebKit - #if os(iOS) + import Models import PSPDFKit import PSPDFKitUI + import SwiftUI + import Utils + import WebKit struct PDFReaderViewController: UIViewControllerRepresentable { + static func registerKey() { + if let pspdfKitKey = AppKeys.sharedInstance?.pspdfKitKey { + SDK.setLicenseKey(pspdfKitKey) + } + } + let document: Document @Environment(\.presentationMode) var presentationMode diff --git a/apple/Sources/PDFViewer.swift b/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewer.swift similarity index 62% rename from apple/Sources/PDFViewer.swift rename to apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewer.swift index 9bfff9d70..ea7b59c0f 100644 --- a/apple/Sources/PDFViewer.swift +++ b/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewer.swift @@ -1,127 +1,148 @@ -import App import Combine import SwiftUI import Utils #if os(iOS) - import PDFKit import PSPDFKit import PSPDFKitUI + import Services struct PDFViewer: View { + final class PDFStateObject: ObservableObject { + @Published var document: Document? + @Published var coordinator: PDFViewCoordinator? + @Published var controllerNeedsConfig = true + } + + @EnvironmentObject var dataService: DataService + struct ShareLink: Identifiable { let id: UUID let url: URL } let pdfURL: URL - let document: Document let viewModel: PDFViewerViewModel - let coordinator: PDFViewCoordinator + + @StateObject var pdfStateObject = PDFStateObject() @State var readerView: Bool = false @State private var shareLink: ShareLink? init(remoteURL: URL, viewModel: PDFViewerViewModel) { self.pdfURL = viewModel.dataURL(remoteURL: remoteURL) self.viewModel = viewModel - self.document = HighlightedDocument(url: pdfURL, viewModel: viewModel) - self.coordinator = PDFViewCoordinator(document: document, viewModel: viewModel) } var body: some View { - PDFView(document: document) - .useParentNavigationBar(true) - .updateConfiguration { builder in - builder.textSelectionShouldSnapToWord = true - } - .updateControllerConfiguration { controller in - print("document is valid", document.isValid) - coordinator.setController(controller: controller) - - // Disable the Document Editor - controller.navigationItem.setRightBarButtonItems( - [controller.thumbnailsButtonItem], - for: .thumbnails, - animated: false - ) - - let barButtonItems = [ - UIBarButtonItem( - image: UIImage(systemName: "textformat"), - style: .plain, - target: controller.settingsButtonItem.target, - action: controller.settingsButtonItem.action - ), - UIBarButtonItem( - image: UIImage(systemName: "book"), - style: .plain, - target: coordinator, - action: #selector(PDFViewCoordinator.toggleReaderView) - ), - UIBarButtonItem( - image: UIImage(systemName: "magnifyingglass"), - style: .plain, - target: controller.searchButtonItem.target, - action: controller.searchButtonItem.action - ) - ] - - document.areAnnotationsEnabled = true - - coordinator.viewer = self - - if viewModel.linkedItem.readingProgressAnchor > 0 { - let pageIndex = UInt(viewModel.linkedItem.readingProgressAnchor) - controller.setPageIndex(pageIndex, animated: false) + if let document = pdfStateObject.document, let coordinator = pdfStateObject.coordinator { + PDFView(document: document) + .useParentNavigationBar(true) + .updateConfiguration { builder in + builder.textSelectionShouldSnapToWord = true } + .updateControllerConfiguration { controller in + // Store config state so we only run this update closure once + guard pdfStateObject.controllerNeedsConfig else { return } + print("document is valid", document.isValid) + coordinator.setController(controller: controller, dataService: dataService) - controller.navigationItem.setRightBarButtonItems(barButtonItems, for: .document, animated: false) - } - .onShouldShowMenuItemsForSelectedText(perform: { pageView, menuItems, selectedText in - let copy = menuItems.first(where: { $0.identifier == "Copy" }) - let highlight = MenuItem(title: "Highlight", block: { - _ = self.coordinator.highlightSelection(pageView: pageView, selectedText: selectedText) - }) + // Disable the Document Editor + controller.navigationItem.setRightBarButtonItems( + [controller.thumbnailsButtonItem], + for: .thumbnails, + animated: false + ) + + let barButtonItems = [ + UIBarButtonItem( + image: UIImage(systemName: "textformat"), + style: .plain, + target: controller.settingsButtonItem.target, + action: controller.settingsButtonItem.action + ), + UIBarButtonItem( + image: UIImage(systemName: "book"), + style: .plain, + target: coordinator, + action: #selector(PDFViewCoordinator.toggleReaderView) + ), + UIBarButtonItem( + image: UIImage(systemName: "magnifyingglass"), + style: .plain, + target: controller.searchButtonItem.target, + action: controller.searchButtonItem.action + ) + ] + + document.areAnnotationsEnabled = true + + coordinator.viewer = self + + if viewModel.pdfItem.readingProgressAnchor > 0 { + let pageIndex = UInt(viewModel.pdfItem.readingProgressAnchor) + controller.setPageIndex(pageIndex, animated: false) + } + + controller.navigationItem.setRightBarButtonItems(barButtonItems, for: .document, animated: false) + pdfStateObject.controllerNeedsConfig = false + } + .onShouldShowMenuItemsForSelectedText(perform: { pageView, menuItems, selectedText in + let copy = menuItems.first(where: { $0.identifier == "Copy" }) + let highlight = MenuItem(title: "Highlight", block: { + _ = coordinator.highlightSelection( + pageView: pageView, + selectedText: selectedText, + dataService: dataService + ) + }) // let share = MenuItem(title: "Share", block: { // let shortId = self.coordinator.highlightSelection(pageView: pageView, selectedText: selectedText) // if let shareURL = viewModel.highlightShareURL(shortId: shortId) { // shareLink = ShareLink(id: UUID(), url: shareURL) // } // }) - return [copy, highlight /* , share */ ].compactMap { $0 } - }) - .onShouldShowMenuItemsForSelectedAnnotations(perform: { _, menuItems, annotations in - var result = [MenuItem]() - if let copy = menuItems.first(where: { $0.identifier == "Copy" }) { - result.append(copy) - } - - let remove = MenuItem(title: "Remove", block: { - self.coordinator.remove(annotations: annotations) + return [copy, highlight /* , share */ ].compactMap { $0 } }) - result.append(remove) + .onShouldShowMenuItemsForSelectedAnnotations(perform: { _, menuItems, annotations in + var result = [MenuItem]() + if let copy = menuItems.first(where: { $0.identifier == "Copy" }) { + result.append(copy) + } - let highlights = annotations?.compactMap { $0 as? HighlightAnnotation } - let shortId = highlights.flatMap { coordinator.shortHighlightIds($0).first } - - if let shortId = shortId, FeatureFlag.enableShareButton { - let share = MenuItem(title: "Share", block: { - if let shareURL = viewModel.highlightShareURL(shortId: shortId) { - shareLink = ShareLink(id: UUID(), url: shareURL) - } + let remove = MenuItem(title: "Remove", block: { + coordinator.remove(dataService: dataService, annotations: annotations) }) - result.append(share) - } + result.append(remove) - return result - }) - .fullScreenCover(isPresented: $readerView, content: { - PDFReaderViewController(document: document) - }) - .accentColor(Color(red: 255 / 255.0, green: 234 / 255.0, blue: 159 / 255.0)) - .sheet(item: $shareLink) { - ShareSheet(activityItems: [$0.url]) - } + let highlights = annotations?.compactMap { $0 as? HighlightAnnotation } + let shortId = highlights.flatMap { coordinator.shortHighlightIds($0).first } + + if let shortId = shortId, FeatureFlag.enableShareButton { + let share = MenuItem(title: "Share", block: { + if let shareURL = viewModel.highlightShareURL(dataService: dataService, shortId: shortId) { + shareLink = ShareLink(id: UUID(), url: shareURL) + } + }) + result.append(share) + } + + return result + }) + .fullScreenCover(isPresented: $readerView, content: { + PDFReaderViewController(document: document) + }) + .accentColor(Color(red: 255 / 255.0, green: 234 / 255.0, blue: 159 / 255.0)) + .sheet(item: $shareLink) { + ShareSheet(activityItems: [$0.url]) + } + } else { + ProgressView() + .task { + let document = HighlightedDocument(url: pdfURL, viewModel: viewModel) + pdfStateObject.document = document + pdfStateObject.coordinator = PDFViewCoordinator(document: document, viewModel: viewModel) + } + } } class PDFViewCoordinator: NSObject, PDFDocumentViewControllerDelegate, PDFViewControllerDelegate { @@ -137,7 +158,7 @@ import Utils self.viewModel = viewModel } - func setController(controller: PDFViewController) { + func setController(controller: PDFViewController, dataService: DataService) { self.controller = controller controller.pageIndexPublisher.sink { event in @@ -145,8 +166,12 @@ import Utils let pageIndex = Int(event.pageIndex) if let totalPageCount = controller.document?.pageCount { let percent = min(100, max(0, ((Double(pageIndex) + 1.0) / Double(totalPageCount)) * 100.0)) - if percent > self.viewModel.linkedItem.readingProgress { - self.viewModel.updateItemReadProgress(percent: percent, anchorIndex: pageIndex) + if percent > self.viewModel.pdfItem.readingProgress { + self.viewModel.updateItemReadProgress( + dataService: dataService, + percent: percent, + anchorIndex: pageIndex + ) } } } @@ -187,7 +212,7 @@ import Utils return result } - func highlightSelection(pageView: PDFPageView, selectedText: String) -> String { + func highlightSelection(pageView: PDFPageView, selectedText: String, dataService: DataService) -> String { let highlightID = UUID().uuidString.lowercased() let quote = quoteFromSelectedText(selectedText) let shortId = NanoID.generate(alphabet: NanoID.Alphabet.urlSafe.rawValue, size: 8) @@ -199,7 +224,7 @@ import Utils "id": highlightID, "shortId": shortId, "quote": quote, - "articleId": viewModel.linkedItem.unwrappedID + "articleId": viewModel.pdfItem.itemID ] ] document.add(annotations: [highlight]) @@ -208,7 +233,13 @@ import Utils if let patchData = try? highlight.generateInstantJSON(), let patch = String(data: patchData, encoding: .utf8) { if overlapping.isEmpty { - viewModel.createHighlight(shortId: shortId, highlightID: highlightID, quote: quote, patch: patch) + viewModel.createHighlight( + dataService: dataService, + shortId: shortId, + highlightID: highlightID, + quote: quote, + patch: patch + ) } else { let overlappingRects = overlapping.map(\.rects).compactMap { $0 }.flatMap { $0 } let rects = overlappingRects + (highlight.rects ?? []) @@ -223,6 +254,7 @@ import Utils document.remove(annotations: overlapping + [highlight]) viewModel.mergeHighlight( + dataService: dataService, shortId: shortId, highlightID: highlightID, quote: quote, @@ -238,10 +270,13 @@ import Utils return shortId } - public func remove(annotations: [Annotation]?) { + public func remove(dataService: DataService, annotations: [Annotation]?) { if let annotations = annotations { document.remove(annotations: annotations) - viewModel.removeHighlights(highlightIds: highlightIds(annotations.compactMap { $0 as? HighlightAnnotation })) + viewModel.removeHighlights( + dataService: dataService, + highlightIds: highlightIds(annotations.compactMap { $0 as? HighlightAnnotation }) + ) } } diff --git a/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift b/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift index 094ec4e4d..2d0b70cbf 100644 --- a/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/PDFSupport/PDFViewerViewModel.swift @@ -8,15 +8,13 @@ public final class PDFViewerViewModel: ObservableObject { @Published public var errorMessage: String? @Published public var readerView: Bool = false - public var linkedItem: LinkedItem + public let pdfItem: PDFItem private var storedURL: URL? var subscriptions = Set() - let services: Services - public init(services: Services, linkedItem: LinkedItem) { - self.services = services - self.linkedItem = linkedItem + public init(pdfItem: PDFItem) { + self.pdfItem = pdfItem } public func dataURL(remoteURL: URL) -> URL { @@ -24,9 +22,9 @@ public final class PDFViewerViewModel: ObservableObject { return storedURL } - guard let data = linkedItem.pdfData else { return remoteURL } + guard let data = pdfItem.documentData else { return remoteURL } - let subPath = linkedItem.unwrappedTitle.isEmpty ? UUID().uuidString : linkedItem.unwrappedTitle + let subPath = pdfItem.title.isEmpty ? UUID().uuidString : pdfItem.title let path = FileManager.default .urls(for: .cachesDirectory, in: .userDomainMask)[0] @@ -42,56 +40,63 @@ public final class PDFViewerViewModel: ObservableObject { } public func loadHighlightPatches(completion onComplete: @escaping ([String]) -> Void) { - onComplete(linkedItem.highlights.asArray(of: Highlight.self).map { $0.patch ?? "" }) + onComplete(pdfItem.highlights.map { $0.patch ?? "" }) } - public func createHighlight(shortId: String, highlightID: String, quote: String, patch: String) { - _ = services.dataService.createHighlight( + public func createHighlight( + dataService: DataService, + shortId: String, + highlightID: String, + quote: String, + patch: String + ) { + _ = dataService.createHighlight( shortId: shortId, highlightID: highlightID, quote: quote, patch: patch, - articleId: linkedItem.unwrappedID + articleId: pdfItem.itemID ) } public func mergeHighlight( + dataService: DataService, shortId: String, highlightID: String, quote: String, patch: String, overlapHighlightIdList: [String] ) { - _ = services.dataService.mergeHighlights( + _ = dataService.mergeHighlights( shortId: shortId, highlightID: highlightID, quote: quote, patch: patch, - articleId: linkedItem.unwrappedID, + articleId: pdfItem.itemID, overlapHighlightIdList: overlapHighlightIdList ) } - public func removeHighlights(highlightIds: [String]) { + public func removeHighlights(dataService: DataService, highlightIds: [String]) { highlightIds.forEach { highlightID in - services.dataService.deleteHighlight(highlightID: highlightID) + dataService.deleteHighlight(highlightID: highlightID) } } - public func updateItemReadProgress(percent: Double, anchorIndex: Int) { - services.dataService.updateLinkReadingProgress( - itemID: linkedItem.unwrappedID, + public func updateItemReadProgress(dataService: DataService, percent: Double, anchorIndex: Int) { + dataService.updateLinkReadingProgress( + itemID: pdfItem.itemID, readingProgress: percent, anchorIndex: anchorIndex ) } - public func highlightShareURL(shortId: String) -> URL? { - let baseURL = services.dataService.appEnvironment.serverBaseURL + public func highlightShareURL(dataService: DataService, shortId: String) -> URL? { + let baseURL = dataService.appEnvironment.serverBaseURL var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false) - if let username = services.dataService.currentViewer?.username { - components?.path = "/\(username)/\(linkedItem.unwrappedSlug)/highlights/\(shortId)" + if let username = dataService.currentViewer?.username { + components?.path = "/\(username)/\(pdfItem.slug)/highlights/\(shortId)" } else { return nil } diff --git a/apple/OmnivoreKit/Sources/App/Services.swift b/apple/OmnivoreKit/Sources/App/Services.swift index c12951763..1a8d6ad59 100644 --- a/apple/OmnivoreKit/Sources/App/Services.swift +++ b/apple/OmnivoreKit/Sources/App/Services.swift @@ -20,61 +20,62 @@ public final class Services { } } -// Background fetching functions -extension Services { - public static func registerBackgroundFetch() { - BGTaskScheduler.shared.register(forTaskWithIdentifier: fetchTaskID, using: nil) { task in - if let task = task as? BGAppRefreshTask { - EventTracker.trackForDebugging("executing app.omnivore.fetchLinkedItems bg task") - logger.debug("in background task register closure") - performBackgroundFetch(task: task) +#if os(iOS) + // Background fetching functions + extension Services { + public static func registerBackgroundFetch() { + BGTaskScheduler.shared.register(forTaskWithIdentifier: fetchTaskID, using: nil) { task in + if let task = task as? BGAppRefreshTask { + EventTracker.trackForDebugging("executing app.omnivore.fetchLinkedItems bg task") + logger.debug("in background task register closure") + performBackgroundFetch(task: task) + } } } - } - static func scheduleBackgroundFetch() { - BGTaskScheduler.shared.cancelAllTaskRequests() - let taskRequest = BGAppRefreshTaskRequest(identifier: fetchTaskID) - taskRequest.earliestBeginDate = Date(timeIntervalSinceNow: secondsToWaitBeforeNextBackgroundRefresh) + static func scheduleBackgroundFetch() { + BGTaskScheduler.shared.cancelAllTaskRequests() + let taskRequest = BGAppRefreshTaskRequest(identifier: fetchTaskID) + taskRequest.earliestBeginDate = Date(timeIntervalSinceNow: secondsToWaitBeforeNextBackgroundRefresh) - do { - try BGTaskScheduler.shared.submit(taskRequest) - logger.debug("\(fetchTaskID) task scheduled") - } catch { - logger.debug("task scheduling failed: \(fetchTaskID)") - } - } - - static func performBackgroundFetch(task: BGAppRefreshTask) { - Services.logger.debug("starting background fetch") - scheduleBackgroundFetch() - let services = Services() - - task.expirationHandler = { - EventTracker.trackForDebugging("background fetch expiration handler called") - logger.debug("handling background fetch expiration") - } - - guard services.authenticator.hasValidAuthToken else { - EventTracker.trackForDebugging("background fetch failed: user does not have a valid auth token") - Services.logger.debug("background fetch failed: user does not habe a valid auth token") - task.setTaskCompleted(success: false) - return - } - - Task { do { - try await services.dataService.fetchLinkedItemsBackgroundTask() - logger.debug("fetch complete") - EventTracker.trackForDebugging("background fetch task completed successfully") - task.setTaskCompleted(success: true) + try BGTaskScheduler.shared.submit(taskRequest) + logger.debug("\(fetchTaskID) task scheduled") } catch { - logger.debug("fetch failed") - EventTracker.trackForDebugging("background fetch task failed") + logger.debug("task scheduling failed: \(fetchTaskID)") + } + } + + static func performBackgroundFetch(task: BGAppRefreshTask) { + Services.logger.debug("starting background fetch") + scheduleBackgroundFetch() + let services = Services() + + task.expirationHandler = { + EventTracker.trackForDebugging("background fetch expiration handler called") + logger.debug("handling background fetch expiration") + } + + guard services.authenticator.hasValidAuthToken else { + EventTracker.trackForDebugging("background fetch failed: user does not have a valid auth token") + Services.logger.debug("background fetch failed: user does not habe a valid auth token") task.setTaskCompleted(success: false) + return + } + + Task { + do { + try await services.dataService.fetchLinkedItemsBackgroundTask() + logger.debug("fetch complete") + EventTracker.trackForDebugging("background fetch task completed successfully") + task.setTaskCompleted(success: true) + } catch { + logger.debug("fetch failed") + EventTracker.trackForDebugging("background fetch task failed") + task.setTaskCompleted(success: false) + } } } } -} - +#endif // e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"app.omnivore.fetchLinkedItems"] diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift index 895216be8..99ee55106 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift @@ -6,12 +6,17 @@ import Views struct FeedCardNavigationLink: View { @EnvironmentObject var dataService: DataService - @ObservedObject var item: LinkedItem + let item: LinkedItem @ObservedObject var viewModel: HomeFeedViewModel var body: some View { - let destination = LinkItemDetailView(viewModel: LinkItemDetailViewModel(item: item)) + let destination = LinkItemDetailView( + viewModel: LinkItemDetailViewModel( + linkedItemObjectID: item.objectID, + dataService: dataService + ) + ) #if os(iOS) let modifiedDestination = destination .navigationTitle("") @@ -22,7 +27,7 @@ struct FeedCardNavigationLink: View { return ZStack { NavigationLink( destination: modifiedDestination, - tag: item, + tag: item.objectID, selection: $viewModel.selectedLinkItem ) { EmptyView() @@ -50,7 +55,12 @@ struct GridCardNavigationLink: View { @ObservedObject var viewModel: HomeFeedViewModel var body: some View { - let destination = LinkItemDetailView(viewModel: LinkItemDetailViewModel(item: item)) + let destination = LinkItemDetailView( + viewModel: LinkItemDetailViewModel( + linkedItemObjectID: item.objectID, + dataService: dataService + ) + ) #if os(iOS) let modifiedDestination = destination .navigationTitle("") @@ -61,7 +71,7 @@ struct GridCardNavigationLink: View { return ZStack { NavigationLink( destination: modifiedDestination, - tag: item, + tag: item.objectID, selection: $viewModel.selectedLinkItem ) { EmptyView() @@ -71,7 +81,7 @@ struct GridCardNavigationLink: View { scale = 0.95 DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(150)) { scale = 1.0 - viewModel.selectedLinkItem = item + viewModel.selectedLinkItem = item.objectID } } }) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index ee531e593..745e1447e 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -5,9 +5,9 @@ import UserNotifications import Utils import Views -private let enableGrid = UIDevice.isIPad || FeatureFlag.enableGridCardsOnPhone - #if os(iOS) + private let enableGrid = UIDevice.isIPad || FeatureFlag.enableGridCardsOnPhone + struct HomeFeedContainerView: View { @EnvironmentObject var dataService: DataService @AppStorage(UserDefaultKey.homeFeedlayoutPreference.rawValue) var prefersListLayout = false @@ -101,7 +101,7 @@ private let enableGrid = UIDevice.isIPad || FeatureFlag.enableGridCardsOnPhone guard let objectID = dataService.persist(jsonArticle: jsonArticle) else { return } guard let linkedItem = dataService.viewContext.object(with: objectID) as? LinkedItem else { return } viewModel.pushFeedItem(item: linkedItem) - viewModel.selectedLinkItem = linkedItem + viewModel.selectedLinkItem = linkedItem.objectID } .onOpenURL { url in withoutAnimation { diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index c11d66d2c..8b2db340f 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -19,7 +19,7 @@ import Views @Published var negatedLabels = [LinkedItemLabel]() @Published var snoozePresented = false @Published var itemToSnoozeID: String? - @Published var selectedLinkItem: LinkedItem? + @Published var selectedLinkItem: NSManagedObjectID? @Published var linkRequest: LinkRequest? @Published var showLoadingBar = false diff --git a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift index e25217235..dea6b1963 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift @@ -1,38 +1,46 @@ import Combine +import CoreData import Models import Services import SwiftUI import Utils import Views -enum PDFProvider { - static var pdfViewerProvider: ((URL, LinkedItem) -> AnyView)? -} - @MainActor final class LinkItemDetailViewModel: ObservableObject { - @Published var item: LinkedItem + let pdfItem: PDFItem? + let item: LinkedItem? @Published var webAppWrapperViewModel: WebAppWrapperViewModel? var subscriptions = Set() - init(item: LinkedItem) { - self.item = item + init(linkedItemObjectID: NSManagedObjectID, dataService: DataService) { + if let linkedItem = dataService.viewContext.object(with: linkedItemObjectID) as? LinkedItem { + self.pdfItem = PDFItem.make(item: linkedItem) + self.item = linkedItem + } else { + self.pdfItem = nil + self.item = nil + } } func handleArchiveAction(dataService: DataService) { - dataService.archiveLink(objectID: item.objectID, archived: !item.isArchived) - Snackbar.show(message: !item.isArchived ? "Link archived" : "Link moved to Inbox") + guard let objectID = item?.objectID ?? pdfItem?.objectID else { return } + dataService.archiveLink(objectID: objectID, archived: !isItemArchived) + Snackbar.show(message: !isItemArchived ? "Link archived" : "Link moved to Inbox") } func handleDeleteAction(dataService: DataService) { + guard let objectID = item?.objectID ?? pdfItem?.objectID else { return } Snackbar.show(message: "Link removed") - dataService.removeLink(objectID: item.objectID) + dataService.removeLink(objectID: objectID) } func updateItemReadStatus(dataService: DataService) { + guard let itemID = item?.unwrappedID ?? pdfItem?.itemID else { return } + dataService.updateLinkReadingProgress( - itemID: item.unwrappedID, - readingProgress: item.isRead ? 0 : 100, + itemID: itemID, + readingProgress: isItemRead ? 0 : 100, anchorIndex: 0 ) } @@ -64,21 +72,34 @@ enum PDFProvider { } func trackReadEvent() { + guard let itemID = item?.unwrappedID ?? pdfItem?.itemID else { return } + guard let slug = item?.unwrappedSlug ?? pdfItem?.slug else { return } + guard let originalArticleURL = item?.unwrappedPageURLString ?? pdfItem?.originalArticleURL else { return } + EventTracker.track( .linkRead( - linkID: item.unwrappedID, - slug: item.unwrappedSlug, - originalArticleURL: item.unwrappedPageURLString + linkID: itemID, + slug: slug, + originalArticleURL: originalArticleURL ) ) } + var isItemRead: Bool { + item?.isRead ?? pdfItem?.isRead ?? false + } + + var isItemArchived: Bool { + item?.isArchived ?? pdfItem?.isArchived ?? false + } + private func createWebAppWrapperViewModel(username: String, dataService: DataService, rawAuthCookie: String?) { + guard let slug = item?.unwrappedSlug ?? pdfItem?.slug else { return } let baseURL = dataService.appEnvironment.webAppBaseURL let urlRequest = URLRequest.webRequest( baseURL: dataService.appEnvironment.webAppBaseURL, - urlPath: "/app/\(username)/\(item.unwrappedSlug)", + urlPath: "/app/\(username)/\(slug)", queryParams: ["isAppEmbedView": "true", "highlightBarDisabled": isMacApp ? "false" : "true"] ) @@ -111,7 +132,7 @@ struct LinkItemDetailView: View { viewModel.updateItemReadStatus(dataService: dataService) }, label: { - Image(systemName: viewModel.item.isRead ? "line.horizontal.3.decrease.circle" : "checkmark.circle") + Image(systemName: viewModel.isItemRead ? "line.horizontal.3.decrease.circle" : "checkmark.circle") } ) } @@ -139,15 +160,15 @@ struct LinkItemDetailView: View { var body: some View { #if os(iOS) - if viewModel.item.isPDF { + if viewModel.pdfItem != nil { fixedNavBarReader .navigationBarHidden(hideNavBar) .task { hideNavBar = true viewModel.trackReadEvent() } - } else { - WebReaderContainerView(item: viewModel.item) + } else if let item = viewModel.item { + WebReaderContainerView(item: item) .navigationBarHidden(hideNavBar) .task { hideNavBar = true @@ -189,8 +210,8 @@ struct LinkItemDetailView: View { action: { viewModel.handleArchiveAction(dataService: dataService) }, label: { Label( - viewModel.item.isArchived ? "Unarchive" : "Archive", - systemImage: viewModel.item.isArchived ? "tray.and.arrow.down.fill" : "archivebox" + viewModel.isItemArchived ? "Unarchive" : "Archive", + systemImage: viewModel.isItemArchived ? "tray.and.arrow.down.fill" : "archivebox" ) } ) @@ -277,9 +298,9 @@ struct LinkItemDetailView: View { #endif @ViewBuilder private var fixedNavBarReader: some View { - if let pdfURL = viewModel.item.pdfURL { + if let pdfItem = viewModel.pdfItem, let pdfURL = pdfItem.pdfURL { #if os(iOS) - PDFProvider.pdfViewerProvider?(pdfURL, viewModel.item) + PDFViewer(remoteURL: pdfURL, viewModel: PDFViewerViewModel(pdfItem: pdfItem)) .navigationBarTitleDisplayMode(.inline) #elseif os(macOS) PDFWrapperView(pdfURL: pdfURL) diff --git a/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift b/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift index 4261c20a1..7ba28267a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift @@ -6,20 +6,18 @@ import Views public struct RootView: View { @Environment(\.scenePhase) var scenePhase - let pdfViewerProvider: ((URL, PDFViewerViewModel) -> AnyView)? @StateObject private var viewModel = RootViewModel() - public init( - pdfViewerProvider: ((URL, PDFViewerViewModel) -> AnyView)?, - intercomProvider: IntercomProvider? - ) { - self.pdfViewerProvider = pdfViewerProvider - + public init(intercomProvider: IntercomProvider?) { if let intercomProvider = intercomProvider { DataService.showIntercomMessenger = intercomProvider.showIntercomMessenger DataService.registerIntercomUser = intercomProvider.registerIntercomUser Authenticator.unregisterIntercomUser = intercomProvider.unregisterIntercomUser } + + #if os(iOS) + PDFReaderViewController.registerKey() + #endif } public var body: some View { @@ -27,14 +25,11 @@ public struct RootView: View { .environmentObject(viewModel.services.authenticator) .environmentObject(viewModel.services.dataService) .environment(\.managedObjectContext, viewModel.services.dataService.viewContext) - .onAppear { - if let pdfViewerProvider = pdfViewerProvider { - viewModel.configurePDFProvider(pdfViewerProvider: pdfViewerProvider) - } - } .onChange(of: scenePhase) { phase in if phase == .background { - Services.scheduleBackgroundFetch() + #if os(iOS) + Services.scheduleBackgroundFetch() + #endif } } } diff --git a/apple/OmnivoreKit/Sources/App/Views/RootView/RootViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/RootView/RootViewModel.swift index cb037610f..b598fa014 100644 --- a/apple/OmnivoreKit/Sources/App/Views/RootView/RootViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/RootView/RootViewModel.swift @@ -32,15 +32,6 @@ public final class RootViewModel: ObservableObject { #endif } - func configurePDFProvider(pdfViewerProvider: @escaping (URL, PDFViewerViewModel) -> AnyView) { - guard PDFProvider.pdfViewerProvider == nil else { return } - - PDFProvider.pdfViewerProvider = { [weak self] url, linkedItem in - guard let self = self else { return AnyView(Text("")) } - return pdfViewerProvider(url, PDFViewerViewModel(services: self.services, linkedItem: linkedItem)) - } - } - func webAppWrapperViewModel(webLinkPath: String) -> WebAppWrapperViewModel { let baseURL = services.dataService.appEnvironment.webAppBaseURL diff --git a/apple/OmnivoreKit/Sources/Models/DataModels/PDFItem.swift b/apple/OmnivoreKit/Sources/Models/DataModels/PDFItem.swift new file mode 100644 index 000000000..2953a2f73 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Models/DataModels/PDFItem.swift @@ -0,0 +1,36 @@ +import CoreData +import Foundation + +public struct PDFItem { + public let objectID: NSManagedObjectID + public let itemID: String + public let pdfURL: URL? + public let documentData: Data? + public let title: String + public let slug: String + public let readingProgress: Double + public let readingProgressAnchor: Int + public let isArchived: Bool + public let isRead: Bool + public let originalArticleURL: String + public let highlights: [Highlight] + + public static func make(item: LinkedItem) -> PDFItem? { + guard item.isPDF else { return nil } + + return PDFItem( + objectID: item.objectID, + itemID: item.unwrappedID, + pdfURL: URL(string: item.unwrappedPageURLString), + documentData: item.pdfData, + title: item.unwrappedID, + slug: item.unwrappedSlug, + readingProgress: item.readingProgress, + readingProgressAnchor: Int(item.readingProgressAnchor), + isArchived: item.isArchived, + isRead: item.isRead, + originalArticleURL: item.unwrappedPageURLString, + highlights: item.highlights.asArray(of: Highlight.self) + ) + } +} diff --git a/apple/OmnivoreKit/Sources/Views/SearchBar.swift b/apple/OmnivoreKit/Sources/Views/SearchBar.swift index 7c7e02a4f..40dd39e73 100644 --- a/apple/OmnivoreKit/Sources/Views/SearchBar.swift +++ b/apple/OmnivoreKit/Sources/Views/SearchBar.swift @@ -15,7 +15,7 @@ public struct SearchBar: View { TextField("Search", text: $searchTerm) .padding(7) .padding(.horizontal, 25) - .background(Color(.systemGray6)) + .background(Color.systemGray6) .cornerRadius(8) .focused($isFocused) .overlay( diff --git a/apple/Sources/MainApp.swift b/apple/Sources/MainApp.swift index 712c76265..909a6be02 100644 --- a/apple/Sources/MainApp.swift +++ b/apple/Sources/MainApp.swift @@ -6,7 +6,6 @@ import SwiftUI import AppKit #elseif os(iOS) import Intercom - import PSPDFKit import UIKit import Utils #endif @@ -17,20 +16,12 @@ struct MainApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate #elseif os(iOS) @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate - - init() { - // Activate PSPDFKit for app.omnivore.app - if let pspdfKitKey = AppKeys.sharedInstance?.pspdfKitKey { - SDK.setLicenseKey(pspdfKitKey) - } - } #endif var body: some Scene { #if os(iOS) WindowGroup { RootView( - pdfViewerProvider: pdfViewerProvider, intercomProvider: AppKeys.sharedInstance?.intercom != nil ? IntercomProvider( registerIntercomUser: { Intercom.registerUser(withUserId: $0) }, unregisterIntercomUser: Intercom.logout, @@ -40,12 +31,8 @@ struct MainApp: App { } #elseif os(macOS) WindowGroup { - RootView(pdfViewerProvider: nil, intercomProvider: nil) + RootView(intercomProvider: nil) } #endif } - - private func pdfViewerProvider(url: URL, viewModel: PDFViewerViewModel) -> AnyView { - AnyView(PDFViewer(remoteURL: url, viewModel: viewModel)) - } }