Selaa lähdekoodia

Gate scan and print features behind the premium paywall.

Non-premium users now see the paywall when opening Scan/Print navigation or any scan, print, and import feature entry point.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 kuukausi sitten
vanhempi
sitoutus
7bfa0c0bb1

+ 3 - 0
smart_printer/DocumentPickerService.swift

@@ -20,6 +20,9 @@ enum DocumentPickerService {
     ]
 
     static func present(_ kind: DocumentPickerKind, from window: NSWindow?) {
+        let hostWindow = window ?? NSApp.keyWindow
+        guard PremiumAccess.require(from: hostWindow) else { return }
+
         let panel = NSOpenPanel()
         panel.canChooseDirectories = false
         panel.canChooseFiles = true

+ 1 - 0
smart_printer/DrawPrintView.swift

@@ -6,6 +6,7 @@ import UniformTypeIdentifiers
 enum DrawPrintService {
     static func present(from window: NSWindow?) {
         let hostWindow = window ?? NSApp.keyWindow
+        guard PremiumAccess.require(from: hostWindow) else { return }
         guard let viewController = hostWindow?.contentViewController as? ViewController else { return }
         viewController.presentDrawPrint()
     }

+ 1 - 0
smart_printer/FilePreviewView.swift

@@ -8,6 +8,7 @@ enum FilePreviewService {
     static func present(urls: [URL], from window: NSWindow?) {
         guard !urls.isEmpty else { return }
         let hostWindow = window ?? NSApp.keyWindow
+        guard PremiumAccess.require(from: hostWindow) else { return }
         guard let viewController = hostWindow?.contentViewController as? ViewController else { return }
         viewController.presentFilePreview(urls: urls)
     }

+ 3 - 0
smart_printer/OCRFileView.swift

@@ -27,6 +27,9 @@ enum OCRFileService {
     ]
 
     static func present(from window: NSWindow?) {
+        let hostWindow = window ?? NSApp.keyWindow
+        guard PremiumAccess.require(from: hostWindow) else { return }
+
         let panel = NSOpenPanel()
         panel.title = "OCR File"
         panel.message = "Select an image to extract text from."

+ 34 - 0
smart_printer/PaywallView.swift

@@ -349,6 +349,40 @@ extension Notification.Name {
     static let storeStateDidChange = Notification.Name("storeStateDidChange")
 }
 
+// MARK: - Premium Access
+
+enum PremiumAccess {
+    @MainActor
+    static var isGranted: Bool {
+        StoreManager.shared.isPremium
+    }
+
+    @MainActor
+    @discardableResult
+    static func require(from window: NSWindow?, returnToHomeOnDismiss: Bool = false) -> Bool {
+        guard !isGranted else { return true }
+        guard let viewController = mainViewController(from: window) else { return false }
+        viewController.presentPaywall(returnToHomeOnDismiss: returnToHomeOnDismiss)
+        return false
+    }
+
+    @MainActor
+    static func mainViewController(from window: NSWindow?) -> ViewController? {
+        if let viewController = window?.contentViewController as? ViewController {
+            return viewController
+        }
+        if let viewController = NSApp.keyWindow?.contentViewController as? ViewController {
+            return viewController
+        }
+        for appWindow in NSApp.windows where appWindow.canBecomeMain {
+            if let viewController = appWindow.contentViewController as? ViewController {
+                return viewController
+            }
+        }
+        return nil
+    }
+}
+
 // MARK: - Left Panel
 
 private final class PaywallLeftPanelView: NSView, AppearanceRefreshable {

+ 1 - 0
smart_printer/PhotoPreviewView.swift

@@ -6,6 +6,7 @@ enum PhotoPreviewService {
     static func present(urls: [URL], from window: NSWindow?) {
         guard !urls.isEmpty else { return }
         let hostWindow = window ?? NSApp.keyWindow
+        guard PremiumAccess.require(from: hostWindow) else { return }
         guard let viewController = hostWindow?.contentViewController as? ViewController else { return }
         viewController.presentPhotoPreview(urls: urls)
     }

+ 1 - 0
smart_printer/PrintContactsView.swift

@@ -155,6 +155,7 @@ enum ContactsService {
 enum PrintContactsService {
     static func present(from window: NSWindow?) {
         let hostWindow = window ?? NSApp.keyWindow
+        guard PremiumAccess.require(from: hostWindow) else { return }
         guard let viewController = hostWindow?.contentViewController as? ViewController else { return }
         viewController.presentPrintContacts()
     }

+ 1 - 0
smart_printer/PrintTextView.swift

@@ -5,6 +5,7 @@ import Cocoa
 enum PrintTextService {
     static func present(from window: NSWindow?) {
         let hostWindow = window ?? NSApp.keyWindow
+        guard PremiumAccess.require(from: hostWindow) else { return }
         guard let viewController = hostWindow?.contentViewController as? ViewController else { return }
         viewController.presentPrintText()
     }

+ 1 - 0
smart_printer/ScanFileView.swift

@@ -33,6 +33,7 @@ enum ScanError: LocalizedError {
 enum ScanFileService {
     static func present(from window: NSWindow?) {
         let hostWindow = window ?? NSApp.keyWindow
+        guard PremiumAccess.require(from: hostWindow) else { return }
         showSourcePicker(from: hostWindow) { source in
             guard let source else { return }
             switch source {

+ 34 - 6
smart_printer/ViewController.swift

@@ -97,6 +97,7 @@ class ViewController: NSViewController {
     }
 
     func presentScanner(showScanPreview: Bool = false, onComplete: @escaping (Result<NSImage, Error>) -> Void) {
+        guard guardPremiumFeature() else { return }
         photoPreviewOverlay?.dismiss(animated: false)
         filePreviewOverlay?.dismiss(animated: false)
         printTextOverlay?.dismiss(animated: false)
@@ -157,6 +158,7 @@ class ViewController: NSViewController {
     }
 
     func presentPrintText() {
+        guard guardPremiumFeature() else { return }
         photoPreviewOverlay?.dismiss(animated: false)
         filePreviewOverlay?.dismiss(animated: false)
         printContactsOverlay?.dismiss(animated: false)
@@ -174,6 +176,7 @@ class ViewController: NSViewController {
     }
 
     func presentPrintContacts() {
+        guard guardPremiumFeature() else { return }
         photoPreviewOverlay?.dismiss(animated: false)
         filePreviewOverlay?.dismiss(animated: false)
         printTextOverlay?.dismiss(animated: false)
@@ -199,6 +202,7 @@ class ViewController: NSViewController {
     }
 
     func presentOCRFile(imageURL: URL) {
+        guard guardPremiumFeature() else { return }
         photoPreviewOverlay?.dismiss(animated: false)
         filePreviewOverlay?.dismiss(animated: false)
         printTextOverlay?.dismiss(animated: false)
@@ -216,6 +220,7 @@ class ViewController: NSViewController {
     }
 
     func presentScanFile(pages: [ScanPage]) {
+        guard guardPremiumFeature() else { return }
         photoPreviewOverlay?.dismiss(animated: false)
         filePreviewOverlay?.dismiss(animated: false)
         printTextOverlay?.dismiss(animated: false)
@@ -242,6 +247,7 @@ class ViewController: NSViewController {
     }
 
     func presentDrawPrint() {
+        guard guardPremiumFeature() else { return }
         photoPreviewOverlay?.dismiss(animated: false)
         filePreviewOverlay?.dismiss(animated: false)
         printTextOverlay?.dismiss(animated: false)
@@ -259,6 +265,7 @@ class ViewController: NSViewController {
     }
 
     func presentPhotoPreview(urls: [URL]) {
+        guard guardPremiumFeature() else { return }
         photoPreviewOverlay?.dismiss(animated: false)
         filePreviewOverlay?.dismiss(animated: false)
         ocrFileOverlay?.dismiss(animated: false)
@@ -277,6 +284,7 @@ class ViewController: NSViewController {
     }
 
     func presentFilePreview(urls: [URL]) {
+        guard guardPremiumFeature() else { return }
         photoPreviewOverlay?.dismiss(animated: false)
         filePreviewOverlay?.dismiss(animated: false)
         ocrFileOverlay?.dismiss(animated: false)
@@ -411,6 +419,11 @@ class ViewController: NSViewController {
     }
 
     private func showDestination(_ destination: SidebarDestination) {
+        if destination == .scan || destination == .print, !StoreManager.shared.isPremium {
+            presentPaywall(returnToHomeOnDismiss: true)
+            return
+        }
+
         dismissFeatureOverlays()
         contentContainer.isHidden = false
 
@@ -426,13 +439,17 @@ class ViewController: NSViewController {
         contentTopBelowWindow.isActive = isSettings
 
         if destination == .scanAndHome {
-            presentPaywall()
+            if StoreManager.shared.isPremium {
+                dismissPaywall()
+            } else {
+                presentPaywall(returnToHomeOnDismiss: true)
+            }
         } else {
             dismissPaywall()
         }
     }
 
-    private func presentPaywall() {
+    func presentPaywall(returnToHomeOnDismiss: Bool = true) {
         if let paywallOverlay {
             Task { @MainActor in
                 await StoreManager.shared.refreshPremiumStatus()
@@ -443,15 +460,26 @@ class ViewController: NSViewController {
         setTrafficLightsHidden(true)
         let overlay = PaywallOverlayView()
         overlay.onDismiss = { [weak self] in
-            self?.setTrafficLightsHidden(false)
-            self?.paywallOverlay = nil
-            self?.sidebar.select(.home)
-            self?.showDestination(.home)
+            guard let self else { return }
+            self.setTrafficLightsHidden(false)
+            self.paywallOverlay = nil
+            if returnToHomeOnDismiss {
+                self.sidebar.select(.home)
+                self.showDestination(.home)
+            }
         }
         overlay.present(in: view)
         paywallOverlay = overlay
     }
 
+    private func guardPremiumFeature() -> Bool {
+        guard StoreManager.shared.isPremium else {
+            presentPaywall(returnToHomeOnDismiss: false)
+            return false
+        }
+        return true
+    }
+
     private func dismissPaywall() {
         paywallOverlay?.removeFromSuperview()
         paywallOverlay = nil