Merge branch 'drop-single-features'

This commit is contained in:
Davide De Rosa 2021-02-07 15:40:11 +01:00
commit c3c3339878
9 changed files with 72 additions and 75 deletions

View File

@ -67,7 +67,7 @@ extension UIViewController {
func presentPurchaseScreen(forProduct product: Product, delegate: PurchaseViewControllerDelegate? = nil) { func presentPurchaseScreen(forProduct product: Product, delegate: PurchaseViewControllerDelegate? = nil) {
let nav = StoryboardScene.Purchase.initialScene.instantiate() let nav = StoryboardScene.Purchase.initialScene.instantiate()
let vc = nav.topViewController as? PurchaseViewController let vc = nav.topViewController as? PurchaseViewController
vc?.feature = product // vc?.feature = product
vc?.delegate = delegate vc?.delegate = delegate
// enforce pre iOS 13 behavior // enforce pre iOS 13 behavior

View File

@ -230,7 +230,7 @@ extension WizardProviderViewController: AccountViewControllerDelegate {
// MARK: - // MARK: -
extension WizardProviderViewController: PurchaseViewControllerDelegate { extension WizardProviderViewController: PurchaseViewControllerDelegate {
func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product) { func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product?) {
guard let metadata = selectedMetadata else { guard let metadata = selectedMetadata else {
return return
} }

View File

@ -32,13 +32,13 @@ import Convenience
private let log = SwiftyBeaver.self private let log = SwiftyBeaver.self
protocol PurchaseViewControllerDelegate: class { protocol PurchaseViewControllerDelegate: class {
func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product) func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product?)
} }
class PurchaseViewController: UITableViewController, StrongTableHost { class PurchaseViewController: UITableViewController, StrongTableHost {
private var isLoading = true private var isLoading = true
var feature: Product! var feature: Product?
weak var delegate: PurchaseViewControllerDelegate? weak var delegate: PurchaseViewControllerDelegate?
@ -66,29 +66,27 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
if let skPlatformVersion = pm.product(withIdentifier: .fullVersion_iOS) { if let skPlatformVersion = pm.product(withIdentifier: .fullVersion_iOS) {
self.skPlatformVersion = skPlatformVersion self.skPlatformVersion = skPlatformVersion
rows.append(.platformVersion) rows.append(.platformVersion)
let bullets: [String] = ProductManager.shared.featureProducts(excluding: [.fullVersion, .fullVersion_iOS, .fullVersion_macOS]).map {
return $0.localizedTitle
}.sortedCaseInsensitive()
platformVersionExtra = bullets.joined(separator: "\n")
} }
if let skFullVersion = pm.product(withIdentifier: .fullVersion) { if !pm.hasPurchased(.fullVersion_macOS), let skFullVersion = pm.product(withIdentifier: .fullVersion) {
self.skFullVersion = skFullVersion self.skFullVersion = skFullVersion
rows.append(.fullVersion) rows.append(.fullVersion)
let bullets: [String] = ProductManager.shared.featureProducts(including: [.fullVersion_iOS, .fullVersion_macOS]).map {
return $0.localizedTitle
}.sortedCaseInsensitive()
fullVersionExtra = bullets.joined(separator: "\n")
} }
if let skFeature = pm.product(withIdentifier: feature) { if let feature = feature, let skFeature = pm.product(withIdentifier: feature) {
self.skFeature = skFeature self.skFeature = skFeature
rows.append(.feature) rows.append(.feature)
} }
rows.append(.restore) rows.append(.restore)
model.set(rows, forSection: .products) model.set(rows, forSection: .products)
let platformBulletsList: [String] = ProductManager.shared.featureProducts(excluding: [.fullVersion, .fullVersion_iOS, .fullVersion_macOS]).map {
return "- \($0.localizedTitle)"
}.sortedCaseInsensitive()
let platformBullets = platformBulletsList.joined(separator: "\n")
platformVersionExtra = "- \(L10n.Core.Purchase.Cells.FullVersion.extraDescription(platformBullets))"
let fullBulletsList: [String] = ProductManager.shared.featureProducts(excluding: [.fullVersion, .fullVersion_iOS]).map {
return "- \($0.localizedTitle)"
}.sortedCaseInsensitive()
let fullBullets = fullBulletsList.joined(separator: "\n")
fullVersionExtra = "- \(L10n.Core.Purchase.Cells.FullVersion.extraDescription(fullBullets))"
} }
// MARK: UIViewController // MARK: UIViewController
@ -96,10 +94,6 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
guard let _ = feature else {
fatalError("No feature set for purchase")
}
title = L10n.Core.Purchase.title title = L10n.Core.Purchase.title
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(close)) navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(close))
@ -168,7 +162,7 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
guard let weakSelf = self else { guard let weakSelf = self else {
return return
} }
let product = weakSelf.feature.matchesStoreKitProduct(skProduct) ? weakSelf.feature! : .fullVersion let product = Product(rawValue: skProduct.productIdentifier)
weakSelf.delegate?.purchaseController(weakSelf, didPurchase: product) weakSelf.delegate?.purchaseController(weakSelf, didPurchase: product)
} }
} }

View File

@ -93,7 +93,7 @@ extension NSAlert {
extension NSViewController { extension NSViewController {
func presentPurchaseScreen(forProduct product: Product, delegate: PurchaseViewControllerDelegate? = nil) { func presentPurchaseScreen(forProduct product: Product, delegate: PurchaseViewControllerDelegate? = nil) {
let vc = StoryboardScene.Purchase.initialScene.instantiate() let vc = StoryboardScene.Purchase.initialScene.instantiate()
vc.feature = product // vc.feature = product
vc.delegate = delegate vc.delegate = delegate
presentAsModalWindow(vc) presentAsModalWindow(vc)
} }

View File

@ -32,7 +32,7 @@ import Convenience
private let log = SwiftyBeaver.self private let log = SwiftyBeaver.self
protocol PurchaseViewControllerDelegate: class { protocol PurchaseViewControllerDelegate: class {
func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product) func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product?)
} }
class PurchaseViewController: NSViewController { class PurchaseViewController: NSViewController {
@ -52,7 +52,7 @@ class PurchaseViewController: NSViewController {
@IBOutlet private weak var buttonRestore: NSButton! @IBOutlet private weak var buttonRestore: NSButton!
var feature: Product! var feature: Product?
weak var delegate: PurchaseViewControllerDelegate? weak var delegate: PurchaseViewControllerDelegate?
@ -74,27 +74,25 @@ class PurchaseViewController: NSViewController {
if let skPlatformVersion = pm.product(withIdentifier: .fullVersion_macOS) { if let skPlatformVersion = pm.product(withIdentifier: .fullVersion_macOS) {
self.skPlatformVersion = skPlatformVersion self.skPlatformVersion = skPlatformVersion
rows.append(.platformVersion) rows.append(.platformVersion)
let bullets: [String] = ProductManager.shared.featureProducts(excluding: [.fullVersion, .fullVersion_iOS, .fullVersion_macOS, .siriShortcuts]).map {
return $0.localizedTitle
}.sortedCaseInsensitive()
platformVersionExtra = bullets.joined(separator: "\n")
} }
if let skFullVersion = pm.product(withIdentifier: .fullVersion) { if !pm.hasPurchased(.fullVersion_iOS), let skFullVersion = pm.product(withIdentifier: .fullVersion) {
self.skFullVersion = skFullVersion self.skFullVersion = skFullVersion
rows.append(.fullVersion) rows.append(.fullVersion)
let bullets: [String] = ProductManager.shared.featureProducts(including: [.fullVersion_iOS, .fullVersion_macOS]).map {
return $0.localizedTitle
}.sortedCaseInsensitive()
fullVersionExtra = bullets.joined(separator: "\n")
} }
if let skFeature = pm.product(withIdentifier: feature) { if let feature = feature, let skFeature = pm.product(withIdentifier: feature) {
self.skFeature = skFeature self.skFeature = skFeature
rows.append(.feature) rows.append(.feature)
} }
let platformBulletsList: [String] = ProductManager.shared.featureProducts(excluding: [.siriShortcuts, .fullVersion, .fullVersion_iOS, .fullVersion_macOS]).map {
return $0.localizedTitle
}.sortedCaseInsensitive()
let platformBullets = platformBulletsList.joined(separator: "\n")
platformVersionExtra = L10n.Core.Purchase.Cells.FullVersion.extraDescription(platformBullets)
let fullBulletsList: [String] = ProductManager.shared.featureProducts(excluding: [.fullVersion, .fullVersion_macOS]).map {
return $0.localizedTitle
}.sortedCaseInsensitive()
let fullBullets = fullBulletsList.joined(separator: "\n")
fullVersionExtra = L10n.Core.Purchase.Cells.FullVersion.extraDescription(fullBullets)
} }
// MARK: NSViewController // MARK: NSViewController
@ -108,10 +106,6 @@ class PurchaseViewController: NSViewController {
buttonPurchase.title = L10n.Core.Purchase.title buttonPurchase.title = L10n.Core.Purchase.title
buttonRestore.title = L10n.Core.Purchase.Cells.Restore.title buttonRestore.title = L10n.Core.Purchase.Cells.Restore.title
guard let _ = feature else {
fatalError("No feature set for purchase")
}
tableView.usesAutomaticRowHeights = true tableView.usesAutomaticRowHeights = true
tableView.reloadData() tableView.reloadData()
} }
@ -197,7 +191,7 @@ class PurchaseViewController: NSViewController {
guard let weakSelf = self else { guard let weakSelf = self else {
return return
} }
let product = weakSelf.feature.matchesStoreKitProduct(skProduct) ? weakSelf.feature! : .fullVersion let product = Product(rawValue: skProduct.productIdentifier)
weakSelf.delegate?.purchaseController(weakSelf, didPurchase: product) weakSelf.delegate?.purchaseController(weakSelf, didPurchase: product)
self?.dismiss(nil) self?.dismiss(nil)

View File

@ -108,7 +108,7 @@ class TrustedNetworksViewController: NSViewController, ProfileCustomization {
@IBAction private func toggleTrustEthernet(_ sender: Any?) { @IBAction private func toggleTrustEthernet(_ sender: Any?) {
do { do {
try ProductManager.shared.verifyEligibleForTrustedNetworks() try ProductManager.shared.verifyEligible(forFeature: .trustedNetworks)
} catch { } catch {
checkTrustEthernet.state = .off checkTrustEthernet.state = .off
presentPurchaseScreen(forProduct: .trustedNetworks) presentPurchaseScreen(forProduct: .trustedNetworks)
@ -132,7 +132,7 @@ class TrustedNetworksViewController: NSViewController, ProfileCustomization {
override func shouldPerformSegue(withIdentifier identifier: NSStoryboardSegue.Identifier, sender: Any?) -> Bool { override func shouldPerformSegue(withIdentifier identifier: NSStoryboardSegue.Identifier, sender: Any?) -> Bool {
if identifier == StoryboardSegue.Service.trustedNetworkAddSegueIdentifier.rawValue { if identifier == StoryboardSegue.Service.trustedNetworkAddSegueIdentifier.rawValue {
do { do {
try ProductManager.shared.verifyEligibleForTrustedNetworks() try ProductManager.shared.verifyEligible(forFeature: .trustedNetworks)
} catch { } catch {
presentPurchaseScreen(forProduct: .trustedNetworks) presentPurchaseScreen(forProduct: .trustedNetworks)
return false return false

View File

@ -554,7 +554,7 @@ public class ConnectionService: Codable {
var rules: [NEOnDemandRule] = [] var rules: [NEOnDemandRule] = []
do { do {
try ProductManager.shared.verifyEligibleForTrustedNetworks() try ProductManager.shared.verifyEligible(forFeature: .trustedNetworks)
#if os(iOS) #if os(iOS)
if profile.trustedNetworks.includesMobile { if profile.trustedNetworks.includesMobile {
let rule = policyRule(for: profile) let rule = policyRule(for: profile)

View File

@ -66,6 +66,8 @@ public struct Product: RawRepresentable, Equatable, Hashable {
// MARK: Features // MARK: Features
public static let allProviders = Product(featureId: "all_providers")
public static let trustedNetworks = Product(featureId: "trusted_networks") public static let trustedNetworks = Product(featureId: "trusted_networks")
public static let siriShortcuts = Product(featureId: "siri") public static let siriShortcuts = Product(featureId: "siri")
@ -77,6 +79,7 @@ public struct Product: RawRepresentable, Equatable, Hashable {
public static let fullVersion = Product(featureId: "full_multi_version") public static let fullVersion = Product(featureId: "full_multi_version")
public static let allFeatures: [Product] = [ public static let allFeatures: [Product] = [
.allProviders,
.trustedNetworks, .trustedNetworks,
.siriShortcuts, .siriShortcuts,
.fullVersion_iOS, .fullVersion_iOS,

View File

@ -126,6 +126,21 @@ public class ProductManager: NSObject {
return inApp.product(withIdentifier: identifier) return inApp.product(withIdentifier: identifier)
} }
public func featureProducts(including: [Product]) -> [SKProduct] {
return inApp.products.filter {
guard let p = Product(rawValue: $0.productIdentifier) else {
return false
}
guard including.contains(p) else {
return false
}
guard p.isFeature else {
return false
}
return true
}
}
public func featureProducts(excluding: [Product]) -> [SKProduct] { public func featureProducts(excluding: [Product]) -> [SKProduct] {
return inApp.products.filter { return inApp.products.filter {
guard let p = Product(rawValue: $0.productIdentifier) else { guard let p = Product(rawValue: $0.productIdentifier) else {
@ -159,29 +174,30 @@ public class ProductManager: NSObject {
// MARK: In-app eligibility // MARK: In-app eligibility
public func isFullVersion() -> Bool { private func isCurrentPlatformVersion() -> Bool {
#if os(iOS) #if os(iOS)
if (isBeta && cfg.isBetaFullVersion) || purchasedFeatures.contains(.fullVersion_iOS) { return purchasedFeatures.contains(.fullVersion_iOS)
return true
}
#else #else
if (isBeta && cfg.isBetaFullVersion) || purchasedFeatures.contains(.fullVersion_macOS) { return purchasedFeatures.contains(.fullVersion_macOS)
#endif
}
private func isFullVersion() -> Bool {
if isBeta && cfg.isBetaFullVersion {
return true
}
if isCurrentPlatformVersion() {
return true return true
} }
#endif
return purchasedFeatures.contains(.fullVersion) return purchasedFeatures.contains(.fullVersion)
} }
private func isEligible(forFeature feature: Product) -> Bool { private func isEligible(forFeature feature: Product) -> Bool {
#if os(iOS)
return isFullVersion() || purchasedFeatures.contains(feature) return isFullVersion() || purchasedFeatures.contains(feature)
} #else
return isFullVersion()
private func isEligible(forProvider metadata: Infrastructure.Metadata) -> Bool { #endif
return isFullVersion() || purchasedFeatures.contains(metadata.product)
}
private func isEligibleForTrustedNetworks() -> Bool {
return isFullVersion() || purchasedFeatures.contains(.trustedNetworks)
} }
public func isEligibleForFeedback() -> Bool { public func isEligibleForFeedback() -> Bool {
@ -211,23 +227,13 @@ public class ProductManager: NSObject {
throw ProductError.beta throw ProductError.beta
} }
} }
guard isFullVersion() || purchasedFeatures.contains(metadata.product) else { guard isEligible(forFeature: metadata.product) else {
throw ProductError.uneligible throw ProductError.uneligible
} }
} }
public func verifyEligibleForTrustedNetworks() throws { public func hasPurchased(_ product: Product) -> Bool {
if isBeta { return purchasedFeatures.contains(product)
if cfg.isBetaFullVersion {
return
}
guard !cfg.locksBetaFeatures else {
throw ProductError.beta
}
}
guard isEligibleForTrustedNetworks() else {
throw ProductError.uneligible
}
} }
public func isCancelledPurchase(_ product: Product) -> Bool { public func isCancelledPurchase(_ product: Product) -> Bool {