Merge branch 'restrict-macos-features'
This commit is contained in:
commit
95449149d3
|
@ -175,9 +175,12 @@
|
||||||
0E57F64620C83FC7008323CF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E57F64420C83FC7008323CF /* LaunchScreen.storyboard */; };
|
0E57F64620C83FC7008323CF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E57F64420C83FC7008323CF /* LaunchScreen.storyboard */; };
|
||||||
0E6268942369AD0600355F75 /* PurchaseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6268932369AD0600355F75 /* PurchaseTableViewCell.swift */; };
|
0E6268942369AD0600355F75 /* PurchaseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6268932369AD0600355F75 /* PurchaseTableViewCell.swift */; };
|
||||||
0E66A270225FE25800F9C779 /* PoolCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E66A26F225FE25800F9C779 /* PoolCategory.swift */; };
|
0E66A270225FE25800F9C779 /* PoolCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E66A26F225FE25800F9C779 /* PoolCategory.swift */; };
|
||||||
|
0E6BA54B25C9EE3A000CDFAC /* Purchase.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E6BA54D25C9EE3A000CDFAC /* Purchase.storyboard */; };
|
||||||
0E6BE13F20CFBAB300A6DD36 /* DebugLogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6BE13E20CFBAB300A6DD36 /* DebugLogViewController.swift */; };
|
0E6BE13F20CFBAB300A6DD36 /* DebugLogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6BE13E20CFBAB300A6DD36 /* DebugLogViewController.swift */; };
|
||||||
0E773BF8224BF37600CDDC8E /* ShortcutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E773BF7224BF37600CDDC8E /* ShortcutsViewController.swift */; };
|
0E773BF8224BF37600CDDC8E /* ShortcutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E773BF7224BF37600CDDC8E /* ShortcutsViewController.swift */; };
|
||||||
0E776642229D0DAE0023FA76 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CAFAD229AAE760008E5C8 /* Intents.intentdefinition */; settings = {ATTRIBUTES = (no_codegen, ); }; };
|
0E776642229D0DAE0023FA76 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CAFAD229AAE760008E5C8 /* Intents.intentdefinition */; settings = {ATTRIBUTES = (no_codegen, ); }; };
|
||||||
|
0E79D2C825C9F1B300D12964 /* PurchaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E79D2C725C9F1B300D12964 /* PurchaseViewController.swift */; };
|
||||||
|
0E79D31E25CC0CF600D12964 /* PurchaseProductView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E79D31D25CC0CF600D12964 /* PurchaseProductView.swift */; };
|
||||||
0E89DFCE213EEDFA00741BA1 /* WizardProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E89DFCD213EEDFA00741BA1 /* WizardProviderViewController.swift */; };
|
0E89DFCE213EEDFA00741BA1 /* WizardProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E89DFCD213EEDFA00741BA1 /* WizardProviderViewController.swift */; };
|
||||||
0E9AA978259F756A003FAFF1 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */; };
|
0E9AA978259F756A003FAFF1 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */; };
|
||||||
0E9AA979259F756A003FAFF1 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */; };
|
0E9AA979259F756A003FAFF1 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */; };
|
||||||
|
@ -485,6 +488,7 @@
|
||||||
0E66A26F225FE25800F9C779 /* PoolCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PoolCategory.swift; path = ../Model/Profiles/PoolCategory.swift; sourceTree = "<group>"; };
|
0E66A26F225FE25800F9C779 /* PoolCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PoolCategory.swift; path = ../Model/Profiles/PoolCategory.swift; sourceTree = "<group>"; };
|
||||||
0E6ACB7722B1A57C001B3C99 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Intents.strings; sourceTree = "<group>"; };
|
0E6ACB7722B1A57C001B3C99 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||||
0E6ACB7822B1A5BB001B3C99 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Core.strings; sourceTree = "<group>"; };
|
0E6ACB7822B1A5BB001B3C99 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Core.strings; sourceTree = "<group>"; };
|
||||||
|
0E6BA54C25C9EE3A000CDFAC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Purchase.storyboard; sourceTree = "<group>"; };
|
||||||
0E6BE13920CFB76800A6DD36 /* ApplicationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationError.swift; sourceTree = "<group>"; };
|
0E6BE13920CFB76800A6DD36 /* ApplicationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationError.swift; sourceTree = "<group>"; };
|
||||||
0E6BE13E20CFBAB300A6DD36 /* DebugLogViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DebugLogViewController.swift; sourceTree = "<group>"; };
|
0E6BE13E20CFBAB300A6DD36 /* DebugLogViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DebugLogViewController.swift; sourceTree = "<group>"; };
|
||||||
0E773BF7224BF37600CDDC8E /* ShortcutsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsViewController.swift; sourceTree = "<group>"; };
|
0E773BF7224BF37600CDDC8E /* ShortcutsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -500,6 +504,8 @@
|
||||||
0E776640229D0DA80023FA76 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Intents.strings; sourceTree = "<group>"; };
|
0E776640229D0DA80023FA76 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||||
0E79D13E21919EC900BB5FB2 /* PlaceholderConnectionProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderConnectionProfile.swift; sourceTree = "<group>"; };
|
0E79D13E21919EC900BB5FB2 /* PlaceholderConnectionProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderConnectionProfile.swift; sourceTree = "<group>"; };
|
||||||
0E79D14021919F5600BB5FB2 /* ProfileKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileKey.swift; sourceTree = "<group>"; };
|
0E79D14021919F5600BB5FB2 /* ProfileKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileKey.swift; sourceTree = "<group>"; };
|
||||||
|
0E79D2C725C9F1B300D12964 /* PurchaseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PurchaseViewController.swift; sourceTree = "<group>"; };
|
||||||
|
0E79D31D25CC0CF600D12964 /* PurchaseProductView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchaseProductView.swift; sourceTree = "<group>"; };
|
||||||
0E89DFC4213DF7AE00741BA1 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
0E89DFC4213DF7AE00741BA1 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
||||||
0E89DFC7213E8FC500741BA1 /* SessionProxy+Communication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+Communication.swift"; sourceTree = "<group>"; };
|
0E89DFC7213E8FC500741BA1 /* SessionProxy+Communication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+Communication.swift"; sourceTree = "<group>"; };
|
||||||
0E89DFCD213EEDFA00741BA1 /* WizardProviderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardProviderViewController.swift; sourceTree = "<group>"; };
|
0E89DFCD213EEDFA00741BA1 /* WizardProviderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardProviderViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -771,6 +777,7 @@
|
||||||
0E569F7D259F41690022DFB8 /* Providers.xcassets */,
|
0E569F7D259F41690022DFB8 /* Providers.xcassets */,
|
||||||
0E569F85259F41690022DFB8 /* Main.storyboard */,
|
0E569F85259F41690022DFB8 /* Main.storyboard */,
|
||||||
0E569F81259F41690022DFB8 /* Preferences.storyboard */,
|
0E569F81259F41690022DFB8 /* Preferences.storyboard */,
|
||||||
|
0E6BA54D25C9EE3A000CDFAC /* Purchase.storyboard */,
|
||||||
0E569F83259F41690022DFB8 /* Service.storyboard */,
|
0E569F83259F41690022DFB8 /* Service.storyboard */,
|
||||||
);
|
);
|
||||||
path = macOS;
|
path = macOS;
|
||||||
|
@ -801,6 +808,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0E569F65259F41690022DFB8 /* Preferences */,
|
0E569F65259F41690022DFB8 /* Preferences */,
|
||||||
|
0E6BA54125C9ED91000CDFAC /* Purchase */,
|
||||||
0E569F6C259F41690022DFB8 /* Service */,
|
0E569F6C259F41690022DFB8 /* Service */,
|
||||||
0E569F69259F41690022DFB8 /* OrganizerProfileTableView.swift */,
|
0E569F69259F41690022DFB8 /* OrganizerProfileTableView.swift */,
|
||||||
0E569F6A259F41690022DFB8 /* OrganizerViewController.swift */,
|
0E569F6A259F41690022DFB8 /* OrganizerViewController.swift */,
|
||||||
|
@ -932,6 +940,15 @@
|
||||||
path = iOS;
|
path = iOS;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
0E6BA54125C9ED91000CDFAC /* Purchase */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
0E79D31D25CC0CF600D12964 /* PurchaseProductView.swift */,
|
||||||
|
0E79D2C725C9F1B300D12964 /* PurchaseViewController.swift */,
|
||||||
|
);
|
||||||
|
path = Purchase;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
0E89DFCC213EEDE700741BA1 /* Organizer */ = {
|
0E89DFCC213EEDE700741BA1 /* Organizer */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -1486,6 +1503,7 @@
|
||||||
0E52031D259F58BF00CBAB56 /* Providers.xcassets in Resources */,
|
0E52031D259F58BF00CBAB56 /* Providers.xcassets in Resources */,
|
||||||
0E52047D259F642600CBAB56 /* Preferences.storyboard in Resources */,
|
0E52047D259F642600CBAB56 /* Preferences.storyboard in Resources */,
|
||||||
0E52038F259F593F00CBAB56 /* App.strings in Resources */,
|
0E52038F259F593F00CBAB56 /* App.strings in Resources */,
|
||||||
|
0E6BA54B25C9EE3A000CDFAC /* Purchase.storyboard in Resources */,
|
||||||
0E520385259F593B00CBAB56 /* Credits.html in Resources */,
|
0E520385259F593B00CBAB56 /* Credits.html in Resources */,
|
||||||
0E52047C259F642600CBAB56 /* Service.storyboard in Resources */,
|
0E52047C259F642600CBAB56 /* Service.storyboard in Resources */,
|
||||||
0E52032B259F58DD00CBAB56 /* TextTableView.xib in Resources */,
|
0E52032B259F58DD00CBAB56 /* TextTableView.xib in Resources */,
|
||||||
|
@ -1882,6 +1900,7 @@
|
||||||
0E294AA225AE2B0B00CB4908 /* Descriptible.swift in Sources */,
|
0E294AA225AE2B0B00CB4908 /* Descriptible.swift in Sources */,
|
||||||
0E520356259F590600CBAB56 /* PreferencesGeneralViewController.swift in Sources */,
|
0E520356259F590600CBAB56 /* PreferencesGeneralViewController.swift in Sources */,
|
||||||
0E520348259F58FE00CBAB56 /* MTUViewController.swift in Sources */,
|
0E520348259F58FE00CBAB56 /* MTUViewController.swift in Sources */,
|
||||||
|
0E79D31E25CC0CF600D12964 /* PurchaseProductView.swift in Sources */,
|
||||||
0E52037E259F593B00CBAB56 /* SwiftGen+Segues.swift in Sources */,
|
0E52037E259F593B00CBAB56 /* SwiftGen+Segues.swift in Sources */,
|
||||||
0E52037C259F593B00CBAB56 /* Theme.swift in Sources */,
|
0E52037C259F593B00CBAB56 /* Theme.swift in Sources */,
|
||||||
0E52035E259F591300CBAB56 /* StatusMenu.swift in Sources */,
|
0E52035E259F591300CBAB56 /* StatusMenu.swift in Sources */,
|
||||||
|
@ -1895,6 +1914,7 @@
|
||||||
0E520354259F590600CBAB56 /* PreferencesViewController.swift in Sources */,
|
0E520354259F590600CBAB56 /* PreferencesViewController.swift in Sources */,
|
||||||
0E520343259F58FE00CBAB56 /* ProfileCustomizationViewController.swift in Sources */,
|
0E520343259F58FE00CBAB56 /* ProfileCustomizationViewController.swift in Sources */,
|
||||||
0E520346259F58FE00CBAB56 /* TrustedNetworksViewController.swift in Sources */,
|
0E520346259F58FE00CBAB56 /* TrustedNetworksViewController.swift in Sources */,
|
||||||
|
0E79D2C825C9F1B300D12964 /* PurchaseViewController.swift in Sources */,
|
||||||
0E520338259F58F500CBAB56 /* ProviderServiceView.swift in Sources */,
|
0E520338259F58F500CBAB56 /* ProviderServiceView.swift in Sources */,
|
||||||
0E520347259F58FE00CBAB56 /* ProxyViewController.swift in Sources */,
|
0E520347259F58FE00CBAB56 /* ProxyViewController.swift in Sources */,
|
||||||
0E52037B259F593B00CBAB56 /* IssueReporter.swift in Sources */,
|
0E52037B259F593B00CBAB56 /* IssueReporter.swift in Sources */,
|
||||||
|
@ -2247,6 +2267,14 @@
|
||||||
name = LaunchScreen.storyboard;
|
name = LaunchScreen.storyboard;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
0E6BA54D25C9EE3A000CDFAC /* Purchase.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
0E6BA54C25C9EE3A000CDFAC /* Base */,
|
||||||
|
);
|
||||||
|
name = Purchase.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
0ED38ADC213F44D00004D387 /* Organizer.storyboard */ = {
|
0ED38ADC213F44D00004D387 /* Organizer.storyboard */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
children = (
|
children = (
|
||||||
|
|
|
@ -95,30 +95,6 @@ internal enum L10n {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
internal enum Purchase {
|
|
||||||
/// Purchase
|
|
||||||
internal static let title = L10n.tr("App", "purchase.title")
|
|
||||||
internal enum Cells {
|
|
||||||
internal enum FullVersion {
|
|
||||||
/// - All providers (including future ones)\n%@
|
|
||||||
internal static func extraDescription(_ p1: Any) -> String {
|
|
||||||
return L10n.tr("App", "purchase.cells.full_version.extra_description", String(describing: p1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal enum Restore {
|
|
||||||
/// If you bought this app or feature in the past, you can restore your purchases and this screen won't show again.
|
|
||||||
internal static let description = L10n.tr("App", "purchase.cells.restore.description")
|
|
||||||
/// Restore purchases
|
|
||||||
internal static let title = L10n.tr("App", "purchase.cells.restore.title")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal enum Sections {
|
|
||||||
internal enum Products {
|
|
||||||
/// Every product is a one-time purchase. Provider purchases do not include a VPN subscription.
|
|
||||||
internal static let footer = L10n.tr("App", "purchase.sections.products.footer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal enum Service {
|
internal enum Service {
|
||||||
internal enum Alerts {
|
internal enum Alerts {
|
||||||
internal enum Location {
|
internal enum Location {
|
||||||
|
@ -765,6 +741,30 @@ internal enum L10n {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
internal enum Purchase {
|
||||||
|
/// Purchase
|
||||||
|
internal static let title = L10n.tr("Core", "purchase.title")
|
||||||
|
internal enum Cells {
|
||||||
|
internal enum FullVersion {
|
||||||
|
/// - All providers (including future ones)\n%@
|
||||||
|
internal static func extraDescription(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Core", "purchase.cells.full_version.extra_description", String(describing: p1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal enum Restore {
|
||||||
|
/// If you bought this app or feature in the past, you can restore your purchases and this screen won't show again.
|
||||||
|
internal static let description = L10n.tr("Core", "purchase.cells.restore.description")
|
||||||
|
/// Restore purchases
|
||||||
|
internal static let title = L10n.tr("Core", "purchase.cells.restore.title")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal enum Sections {
|
||||||
|
internal enum Products {
|
||||||
|
/// Every product is a one-time purchase. Provider purchases do not include a VPN subscription.
|
||||||
|
internal static let footer = L10n.tr("Core", "purchase.sections.products.footer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
internal enum Reddit {
|
internal enum Reddit {
|
||||||
/// Did you know that Passepartout has a subreddit? Subscribe for updates or to discuss issues, features, new platforms or whatever you like.\n\nIt's also a great way to show you care about this project.
|
/// Did you know that Passepartout has a subreddit? Subscribe for updates or to discuss issues, features, new platforms or whatever you like.\n\nIt's also a great way to show you care about this project.
|
||||||
internal static let message = L10n.tr("Core", "reddit.message")
|
internal static let message = L10n.tr("Core", "reddit.message")
|
||||||
|
|
|
@ -43,9 +43,13 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
|
||||||
weak var delegate: PurchaseViewControllerDelegate?
|
weak var delegate: PurchaseViewControllerDelegate?
|
||||||
|
|
||||||
private var skFeature: SKProduct?
|
private var skFeature: SKProduct?
|
||||||
|
|
||||||
|
private var skPlatformVersion: SKProduct?
|
||||||
|
|
||||||
private var skFullVersion: SKProduct?
|
private var skFullVersion: SKProduct?
|
||||||
|
|
||||||
|
private var platformVersionExtra: String?
|
||||||
|
|
||||||
private var fullVersionExtra: String?
|
private var fullVersionExtra: String?
|
||||||
|
|
||||||
// MARK: StrongTableHost
|
// MARK: StrongTableHost
|
||||||
|
@ -55,10 +59,14 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
|
||||||
func reloadModel() {
|
func reloadModel() {
|
||||||
model.clear()
|
model.clear()
|
||||||
model.add(.products)
|
model.add(.products)
|
||||||
model.setFooter(L10n.App.Purchase.Sections.Products.footer, forSection: .products)
|
model.setFooter(L10n.Core.Purchase.Sections.Products.footer, forSection: .products)
|
||||||
|
|
||||||
var rows: [RowType] = []
|
var rows: [RowType] = []
|
||||||
let pm = ProductManager.shared
|
let pm = ProductManager.shared
|
||||||
|
if let skPlatformVersion = pm.product(withIdentifier: .fullVersion_iOS) {
|
||||||
|
self.skPlatformVersion = skPlatformVersion
|
||||||
|
rows.append(.platformVersion)
|
||||||
|
}
|
||||||
if let skFullVersion = pm.product(withIdentifier: .fullVersion) {
|
if let skFullVersion = pm.product(withIdentifier: .fullVersion) {
|
||||||
self.skFullVersion = skFullVersion
|
self.skFullVersion = skFullVersion
|
||||||
rows.append(.fullVersion)
|
rows.append(.fullVersion)
|
||||||
|
@ -70,11 +78,17 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
|
||||||
rows.append(.restore)
|
rows.append(.restore)
|
||||||
model.set(rows, forSection: .products)
|
model.set(rows, forSection: .products)
|
||||||
|
|
||||||
let featureBulletsList: [String] = ProductManager.shared.featureProducts(excluding: [.fullVersion, .fullVersion_iOS]).map {
|
let platformBulletsList: [String] = ProductManager.shared.featureProducts(excluding: [.fullVersion, .fullVersion_iOS, .fullVersion_macOS]).map {
|
||||||
return "- \($0.localizedTitle)"
|
return "- \($0.localizedTitle)"
|
||||||
}.sortedCaseInsensitive()
|
}.sortedCaseInsensitive()
|
||||||
let featureBullets = featureBulletsList.joined(separator: "\n")
|
let platformBullets = platformBulletsList.joined(separator: "\n")
|
||||||
fullVersionExtra = L10n.App.Purchase.Cells.FullVersion.extraDescription(featureBullets)
|
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
|
||||||
|
@ -86,7 +100,7 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
|
||||||
fatalError("No feature set for purchase")
|
fatalError("No feature set for purchase")
|
||||||
}
|
}
|
||||||
|
|
||||||
title = L10n.App.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))
|
||||||
|
|
||||||
isLoading = true
|
isLoading = true
|
||||||
|
@ -113,6 +127,13 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
|
||||||
}
|
}
|
||||||
purchase(sk)
|
purchase(sk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func purchasePlatformVersion() {
|
||||||
|
guard let sk = skPlatformVersion else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
purchase(sk)
|
||||||
|
}
|
||||||
|
|
||||||
private func purchaseFullVersion() {
|
private func purchaseFullVersion() {
|
||||||
guard let sk = skFullVersion else {
|
guard let sk = skFullVersion else {
|
||||||
|
@ -174,8 +195,10 @@ extension PurchaseViewController {
|
||||||
enum RowType {
|
enum RowType {
|
||||||
case feature
|
case feature
|
||||||
|
|
||||||
case fullVersion
|
case platformVersion
|
||||||
|
|
||||||
|
case fullVersion
|
||||||
|
|
||||||
case restore
|
case restore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +226,12 @@ extension PurchaseViewController {
|
||||||
}
|
}
|
||||||
cell.fill(product: product)
|
cell.fill(product: product)
|
||||||
|
|
||||||
|
case .platformVersion:
|
||||||
|
guard let product = skPlatformVersion else {
|
||||||
|
fatalError("Loaded platform version cell, yet no corresponding product?")
|
||||||
|
}
|
||||||
|
cell.fill(product: product, customDescription: platformVersionExtra)
|
||||||
|
|
||||||
case .fullVersion:
|
case .fullVersion:
|
||||||
guard let product = skFullVersion else {
|
guard let product = skFullVersion else {
|
||||||
fatalError("Loaded full version cell, yet no corresponding product?")
|
fatalError("Loaded full version cell, yet no corresponding product?")
|
||||||
|
@ -211,8 +240,8 @@ extension PurchaseViewController {
|
||||||
|
|
||||||
case .restore:
|
case .restore:
|
||||||
cell.fill(
|
cell.fill(
|
||||||
title: L10n.App.Purchase.Cells.Restore.title,
|
title: L10n.Core.Purchase.Cells.Restore.title,
|
||||||
description: L10n.App.Purchase.Cells.Restore.description
|
description: L10n.Core.Purchase.Cells.Restore.description
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return cell
|
return cell
|
||||||
|
@ -225,6 +254,9 @@ extension PurchaseViewController {
|
||||||
case .feature:
|
case .feature:
|
||||||
purchaseFeature()
|
purchaseFeature()
|
||||||
|
|
||||||
|
case .platformVersion:
|
||||||
|
purchasePlatformVersion()
|
||||||
|
|
||||||
case .fullVersion:
|
case .fullVersion:
|
||||||
purchaseFullVersion()
|
purchaseFullVersion()
|
||||||
|
|
||||||
|
|
|
@ -63,9 +63,3 @@
|
||||||
|
|
||||||
"shortcuts.edit.title" = "Kurzbefehle bearbeiten";
|
"shortcuts.edit.title" = "Kurzbefehle bearbeiten";
|
||||||
"shortcuts.edit.cells.add_shortcut.caption" = "Kurzbefehl hinzufügen";
|
"shortcuts.edit.cells.add_shortcut.caption" = "Kurzbefehl hinzufügen";
|
||||||
|
|
||||||
"purchase.title" = "Kaufen";
|
|
||||||
"purchase.sections.products.footer" = "Jedes Produkt ist ein einmaliger Kauf. Der Kauf eines Providers beinhaltet kein VPN-Abonnement.";
|
|
||||||
"purchase.cells.full_version.extra_description" = "- Alle Anbieter (inklusive Zukünftige)\n%@";
|
|
||||||
"purchase.cells.restore.title" = "Einkäufe wiederherstellen";
|
|
||||||
"purchase.cells.restore.description" = "Wenn Sie diese App oder Funktion in der Vergangenheit gekauft haben, können Sie Ihre Einkäufe wiederherstellen und dieser Bildschirm wird nicht mehr angezeigt.";
|
|
||||||
|
|
|
@ -63,9 +63,3 @@
|
||||||
|
|
||||||
"shortcuts.edit.title" = "Διαχείριση συντομεύσεων";
|
"shortcuts.edit.title" = "Διαχείριση συντομεύσεων";
|
||||||
"shortcuts.edit.cells.add_shortcut.caption" = "Προσθήκη Συντόμευσης";
|
"shortcuts.edit.cells.add_shortcut.caption" = "Προσθήκη Συντόμευσης";
|
||||||
|
|
||||||
"purchase.title" = "Αγορά";
|
|
||||||
"purchase.sections.products.footer" = "Κάθε προϊόν είναι μια αγορά. Οι αγορές παρόχων δεν περιλαμβάνουν τη συνδρομή VPN.";
|
|
||||||
"purchase.cells.full_version.extra_description" = "- Όλοι οι πάροχοι (περιλαμβάνονται και οι μελλοντικοί)\n%@";
|
|
||||||
"purchase.cells.restore.title" = "Επαναφορά Αγορών";
|
|
||||||
"purchase.cells.restore.description" = "Εαν αγοράσατε την εφαρμογή στο παρελθόν, μπορείτε να κάνετε επαναφορά αγορών και αυτή η οθόνη δε θα εμφανιστεί ξανά.";
|
|
||||||
|
|
|
@ -63,9 +63,3 @@
|
||||||
|
|
||||||
"shortcuts.edit.title" = "Manage shortcuts";
|
"shortcuts.edit.title" = "Manage shortcuts";
|
||||||
"shortcuts.edit.cells.add_shortcut.caption" = "Add shortcut";
|
"shortcuts.edit.cells.add_shortcut.caption" = "Add shortcut";
|
||||||
|
|
||||||
"purchase.title" = "Purchase";
|
|
||||||
"purchase.sections.products.footer" = "Every product is a one-time purchase. Provider purchases do not include a VPN subscription.";
|
|
||||||
"purchase.cells.full_version.extra_description" = "- All providers (including future ones)\n%@";
|
|
||||||
"purchase.cells.restore.title" = "Restore purchases";
|
|
||||||
"purchase.cells.restore.description" = "If you bought this app or feature in the past, you can restore your purchases and this screen won't show again.";
|
|
||||||
|
|
|
@ -63,9 +63,3 @@
|
||||||
|
|
||||||
"shortcuts.edit.title" = "Gestionar atajos";
|
"shortcuts.edit.title" = "Gestionar atajos";
|
||||||
"shortcuts.edit.cells.add_shortcut.caption" = "Añadir atajo";
|
"shortcuts.edit.cells.add_shortcut.caption" = "Añadir atajo";
|
||||||
|
|
||||||
"purchase.title" = "Compra";
|
|
||||||
"purchase.sections.products.footer" = "Cada producto es una compra única y no recurrente. La compra de un proveedor no incluye una suscripción al servicio.";
|
|
||||||
"purchase.cells.full_version.extra_description" = "- Todos los proveedores (incluye los futuros)\n%@";
|
|
||||||
"purchase.cells.restore.title" = "Restaurar compras";
|
|
||||||
"purchase.cells.restore.description" = "Si compraste esta aplicación o funcionalidad anteriormente, puedes restaurar tus compras y esta pantalla no volverá a aparecer.";
|
|
||||||
|
|
|
@ -63,9 +63,3 @@
|
||||||
|
|
||||||
"shortcuts.edit.title" = "Gérer les raccourcis";
|
"shortcuts.edit.title" = "Gérer les raccourcis";
|
||||||
"shortcuts.edit.cells.add_shortcut.caption" = "Ajouter un raccourcis";
|
"shortcuts.edit.cells.add_shortcut.caption" = "Ajouter un raccourcis";
|
||||||
|
|
||||||
"purchase.title" = "Acheter";
|
|
||||||
"purchase.sections.products.footer" = "Chaque produit est un achat unique. Les achats n'incluent pas une souscription à un service de VPN.";
|
|
||||||
"purchase.cells.full_version.extra_description" = "- Tous les fournisseurs (incluant les prochains)\n%@";
|
|
||||||
"purchase.cells.restore.title" = "Restaurer les achats";
|
|
||||||
"purchase.cells.restore.description" = "Si vous avez acheté l'application ou une fonctionnalité dans le passé, vous pouvez restaurer les achats et ce message ne s'affichera plus.";
|
|
||||||
|
|
|
@ -63,9 +63,3 @@
|
||||||
|
|
||||||
"shortcuts.edit.title" = "Gestisci comandi rapidi";
|
"shortcuts.edit.title" = "Gestisci comandi rapidi";
|
||||||
"shortcuts.edit.cells.add_shortcut.caption" = "Aggiungi comando rapido";
|
"shortcuts.edit.cells.add_shortcut.caption" = "Aggiungi comando rapido";
|
||||||
|
|
||||||
"purchase.title" = "Acquista";
|
|
||||||
"purchase.sections.products.footer" = "Ogni prodotto è un acquisto unico e non ricorrente. L'acquisto di un provider non include una sottoscrizione.";
|
|
||||||
"purchase.cells.full_version.extra_description" = "- Tutti i provider (inclusi quelli futuri)\n%@";
|
|
||||||
"purchase.cells.restore.title" = "Ripristina acquisti";
|
|
||||||
"purchase.cells.restore.description" = "Se hai comprato quest'applicazione o funzionalità in precedenza, puoi ripristinare i tuoi acquisti in modo che questa schermata non compaia più.";
|
|
||||||
|
|
|
@ -63,9 +63,3 @@
|
||||||
|
|
||||||
"shortcuts.edit.title" = "Beheer snelkoppelingen";
|
"shortcuts.edit.title" = "Beheer snelkoppelingen";
|
||||||
"shortcuts.edit.cells.add_shortcut.caption" = "Voeg snelkoppeling toe";
|
"shortcuts.edit.cells.add_shortcut.caption" = "Voeg snelkoppeling toe";
|
||||||
|
|
||||||
"purchase.title" = "Aanschaffen";
|
|
||||||
"purchase.sections.products.footer" = "Elk product is een eenmalige aankoop. Aankopen van providers bevatten geen VPN-abonnement.";
|
|
||||||
"purchase.cells.full_version.extra_description" = "- Alle providers (inclusief toekomstige)\n%@";
|
|
||||||
"purchase.cells.restore.title" = "Herstel Aankopen";
|
|
||||||
"purchase.cells.restore.description" = "Als u deze app of functie in het verleden heeft gekocht, kunt u uw aankopen herstellen en wordt dit scherm niet meer getoond.";
|
|
||||||
|
|
|
@ -63,9 +63,3 @@
|
||||||
|
|
||||||
"shortcuts.edit.title" = "Zarządzaj skrótami";
|
"shortcuts.edit.title" = "Zarządzaj skrótami";
|
||||||
"shortcuts.edit.cells.add_shortcut.caption" = "Dodaj skrót";
|
"shortcuts.edit.cells.add_shortcut.caption" = "Dodaj skrót";
|
||||||
|
|
||||||
"purchase.title" = "Kup";
|
|
||||||
"purchase.sections.products.footer" = "Każdy produkt to zakup jednorazowy. Kuipno usługodawcy nie zawiera subskrypcji VPN.";
|
|
||||||
"purchase.cells.full_version.extra_description" = "- Wszyscy usługodawcy (włączając przyszłych)\n%@";
|
|
||||||
"purchase.cells.restore.title" = "Przywróć zakup";
|
|
||||||
"purchase.cells.restore.description" = "Jeśli kupiłeś tą aplikację lub funkcję wcześniej, możesz przywrócić swoje zakupy i ten ekran nie będzie wyświetlony ponownie.";
|
|
||||||
|
|
|
@ -63,9 +63,3 @@
|
||||||
|
|
||||||
"shortcuts.edit.title" = "Configuração de atalhos";
|
"shortcuts.edit.title" = "Configuração de atalhos";
|
||||||
"shortcuts.edit.cells.add_shortcut.caption" = "Adicionar atalho";
|
"shortcuts.edit.cells.add_shortcut.caption" = "Adicionar atalho";
|
||||||
|
|
||||||
"purchase.title" = "Comprar";
|
|
||||||
"purchase.sections.products.footer" = "Todo produto é uma compra única. As compras do fornecedor não incluem uma assinatura VPN.";
|
|
||||||
"purchase.cells.full_version.extra_description" = "- Todos os provedores (incluindo os futuros)\n%@";
|
|
||||||
"purchase.cells.restore.title" = "Restaurar compras";
|
|
||||||
"purchase.cells.restore.description" = "Se você comprou este aplicativo ou recurso no passado, pode restaurar suas compras e essa tela não será exibida novamente.";
|
|
||||||
|
|
|
@ -63,9 +63,3 @@
|
||||||
|
|
||||||
"shortcuts.edit.title" = "Управлять командами";
|
"shortcuts.edit.title" = "Управлять командами";
|
||||||
"shortcuts.edit.cells.add_shortcut.caption" = "Создать команду";
|
"shortcuts.edit.cells.add_shortcut.caption" = "Создать команду";
|
||||||
|
|
||||||
"purchase.title" = "Покупка";
|
|
||||||
"purchase.sections.products.footer" = "Каждый продукт является разовой покупкой. Покупка провайдера не включает подписку на VPN.";
|
|
||||||
"purchase.cells.full_version.extra_description" = "- Все провайдеры (включая добавленных в будущем)\n%@";
|
|
||||||
"purchase.cells.restore.title" = "Восстановить покупки";
|
|
||||||
"purchase.cells.restore.description" = "Если Вы купили это приложение или совершили встроенные покупки в прошлом, вы можете восстановить ваши покупки, и этот баннер больше не появится.";
|
|
||||||
|
|
|
@ -63,9 +63,3 @@
|
||||||
|
|
||||||
"shortcuts.edit.title" = "Hantera genvägar";
|
"shortcuts.edit.title" = "Hantera genvägar";
|
||||||
"shortcuts.edit.cells.add_shortcut.caption" = "Lägg till genväg";
|
"shortcuts.edit.cells.add_shortcut.caption" = "Lägg till genväg";
|
||||||
|
|
||||||
"purchase.title" = "Köp";
|
|
||||||
"purchase.sections.products.footer" = "Varje produkt är ett engångsköp. Leverantörsköp inkluderar inte ett VPN-abonnemang.";
|
|
||||||
"purchase.cells.full_version.extra_description" = "- Alla leverantörer (inklusive framtida)\n%@";
|
|
||||||
"purchase.cells.restore.title" = "Återställ köp";
|
|
||||||
"purchase.cells.restore.description" = "Om du köpte den här appen eller funktionen tidigare kan du återställa dina inköp och den här skärmen visas inte igen.";
|
|
||||||
|
|
|
@ -63,9 +63,3 @@
|
||||||
|
|
||||||
"shortcuts.edit.title" = "管理捷径";
|
"shortcuts.edit.title" = "管理捷径";
|
||||||
"shortcuts.edit.cells.add_shortcut.caption" = "添加捷径";
|
"shortcuts.edit.cells.add_shortcut.caption" = "添加捷径";
|
||||||
|
|
||||||
"purchase.title" = "购买";
|
|
||||||
"purchase.sections.products.footer" = "每件产品都是一次性的购买。 购买的提供商并不包含VPN订阅。";
|
|
||||||
"purchase.cells.full_version.extra_description" = "- 所有的提供商(包括未来添加的)\n%@";
|
|
||||||
"purchase.cells.restore.title" = "恢复购买";
|
|
||||||
"purchase.cells.restore.description" = "如果你购买过此应用或其特征, 你可以恢复购买,此页面将不在显示。";
|
|
||||||
|
|
|
@ -57,6 +57,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
NSApp.mainMenu = loadMainMenu()
|
NSApp.mainMenu = loadMainMenu()
|
||||||
StatusMenu.shared.install()
|
StatusMenu.shared.install()
|
||||||
|
ProductManager.shared.reviewPurchases()
|
||||||
|
|
||||||
// if let appCenterSecret = appCenterSecret, !appCenterSecret.isEmpty {
|
// if let appCenterSecret = appCenterSecret, !appCenterSecret.isEmpty {
|
||||||
// AppCenter.start(withAppSecret: appCenterSecret, services: [Analytics.self, Crashes.self])
|
// AppCenter.start(withAppSecret: appCenterSecret, services: [Analytics.self, Crashes.self])
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="Rv5-Zx-TH3">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17701"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Purchase View Controller-->
|
||||||
|
<scene sceneID="9TJ-0E-yCE">
|
||||||
|
<objects>
|
||||||
|
<viewController id="Rv5-Zx-TH3" customClass="PurchaseViewController" customModule="Passepartout" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="8QZ-37-H7U">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="442" height="500"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<scrollView autohidesScrollers="YES" horizontalLineScroll="80" horizontalPageScroll="10" verticalLineScroll="80" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WTb-Wq-BLo">
|
||||||
|
<rect key="frame" x="20" y="120" width="402" height="360"/>
|
||||||
|
<clipView key="contentView" id="dfi-7t-ces">
|
||||||
|
<rect key="frame" x="1" y="1" width="400" height="358"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="80" rowSizeStyle="automatic" viewBased="YES" id="9w7-b6-jXh">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="400" height="358"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<size key="intercellSpacing" width="17" height="0.0"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<tableColumns>
|
||||||
|
<tableColumn width="388" minWidth="40" maxWidth="1000" id="GRZ-an-Pd1">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" title="Text" id="gwL-5E-ehb">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<customView identifier="ProductCellIdentifier" misplaced="YES" id="pnP-i1-QiM" customClass="PurchaseProductView" customModule="Passepartout" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="8" y="0.0" width="383" height="80"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="q6k-22-i5F">
|
||||||
|
<rect key="frame" x="18" y="44" width="278" height="20"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="<title>" id="3zZ-WV-GIN">
|
||||||
|
<font key="font" metaFont="systemMedium" size="17"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xYH-wg-hUh">
|
||||||
|
<rect key="frame" x="18" y="20" width="347" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="<description>" id="bqY-NM-eTC">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kRO-hG-JPd">
|
||||||
|
<rect key="frame" x="300" y="44" width="65" height="20"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="<price>" id="YYL-xk-bbo">
|
||||||
|
<font key="font" metaFont="systemMedium" size="17"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="xYH-wg-hUh" secondAttribute="bottom" constant="20" symbolic="YES" id="1EP-V1-gsU"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="xYH-wg-hUh" secondAttribute="trailing" constant="20" symbolic="YES" id="Des-Zp-REX"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="kRO-hG-JPd" secondAttribute="trailing" constant="20" symbolic="YES" id="OY4-cV-sdC"/>
|
||||||
|
<constraint firstItem="kRO-hG-JPd" firstAttribute="leading" secondItem="q6k-22-i5F" secondAttribute="trailing" constant="8" symbolic="YES" id="Olt-aL-NpR"/>
|
||||||
|
<constraint firstItem="xYH-wg-hUh" firstAttribute="top" secondItem="q6k-22-i5F" secondAttribute="bottom" constant="8" symbolic="YES" id="R2x-ie-ECU"/>
|
||||||
|
<constraint firstItem="q6k-22-i5F" firstAttribute="leading" secondItem="pnP-i1-QiM" secondAttribute="leading" constant="20" symbolic="YES" id="TQm-gM-KGy"/>
|
||||||
|
<constraint firstItem="q6k-22-i5F" firstAttribute="top" secondItem="pnP-i1-QiM" secondAttribute="top" constant="20" symbolic="YES" id="dO0-4G-6TX"/>
|
||||||
|
<constraint firstItem="kRO-hG-JPd" firstAttribute="centerY" secondItem="q6k-22-i5F" secondAttribute="centerY" id="f9p-8K-1bF"/>
|
||||||
|
<constraint firstItem="xYH-wg-hUh" firstAttribute="leading" secondItem="pnP-i1-QiM" secondAttribute="leading" constant="20" symbolic="YES" id="nZu-CX-k7N"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="labelDescription" destination="xYH-wg-hUh" id="TSs-OW-1hh"/>
|
||||||
|
<outlet property="labelPrice" destination="kRO-hG-JPd" id="QE3-Yk-pxQ"/>
|
||||||
|
<outlet property="labelTitle" destination="q6k-22-i5F" id="0IP-Gv-Wh0"/>
|
||||||
|
</connections>
|
||||||
|
</customView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
</tableColumns>
|
||||||
|
<connections>
|
||||||
|
<outlet property="dataSource" destination="Rv5-Zx-TH3" id="QpY-8y-snp"/>
|
||||||
|
<outlet property="delegate" destination="Rv5-Zx-TH3" id="vc2-tf-15J"/>
|
||||||
|
</connections>
|
||||||
|
</tableView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="CeE-dS-NyP">
|
||||||
|
<rect key="frame" x="1" y="329" width="400" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="2Ip-OW-X0v">
|
||||||
|
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="TKV-I9-3N8">
|
||||||
|
<rect key="frame" x="19" y="96" width="404" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="400" id="uyO-So-B3k"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" title="<footer>" id="thS-No-Evy">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<progressIndicator hidden="YES" maxValue="100" displayedWhenStopped="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="2hB-Jy-QEw">
|
||||||
|
<rect key="frame" x="213" y="22" width="16" height="16"/>
|
||||||
|
</progressIndicator>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="XMI-x0-24k">
|
||||||
|
<rect key="frame" x="230" y="13" width="94" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="<restore>" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Tce-5t-Hj1">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="doRestorePurchases:" target="Rv5-Zx-TH3" id="5Nz-PR-cvY"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lBO-a6-qnj">
|
||||||
|
<rect key="frame" x="322" y="13" width="107" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="<purchase>" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="uxN-5f-y4i">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="doPurchase:" target="Rv5-Zx-TH3" id="e5O-mz-tRo"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="72F-hB-DPP">
|
||||||
|
<rect key="frame" x="19" y="60" width="404" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="400" id="ejH-6r-pkl"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" title="<restore>" id="VuC-Wl-HpD">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="72F-hB-DPP" firstAttribute="top" secondItem="TKV-I9-3N8" secondAttribute="bottom" constant="20" id="8kK-yQ-UGc"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="lBO-a6-qnj" secondAttribute="bottom" constant="20" symbolic="YES" id="ENp-WT-Z1C"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="WTb-Wq-BLo" secondAttribute="trailing" constant="20" symbolic="YES" id="Ed7-QK-8qI"/>
|
||||||
|
<constraint firstItem="lBO-a6-qnj" firstAttribute="leading" secondItem="XMI-x0-24k" secondAttribute="trailing" constant="12" symbolic="YES" id="HGl-z7-xCS"/>
|
||||||
|
<constraint firstItem="XMI-x0-24k" firstAttribute="centerY" secondItem="lBO-a6-qnj" secondAttribute="centerY" id="Lkd-1j-rt9"/>
|
||||||
|
<constraint firstItem="lBO-a6-qnj" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="9w7-b6-jXh" secondAttribute="leading" id="PAx-YO-dub"/>
|
||||||
|
<constraint firstItem="TKV-I9-3N8" firstAttribute="trailing" secondItem="9w7-b6-jXh" secondAttribute="trailing" id="QMH-Dc-Vbo"/>
|
||||||
|
<constraint firstItem="XMI-x0-24k" firstAttribute="leading" secondItem="2hB-Jy-QEw" secondAttribute="trailing" constant="8" symbolic="YES" id="Rlz-6Y-GTq"/>
|
||||||
|
<constraint firstItem="WTb-Wq-BLo" firstAttribute="leading" secondItem="8QZ-37-H7U" secondAttribute="leading" constant="20" symbolic="YES" id="UZT-z0-YQI"/>
|
||||||
|
<constraint firstItem="72F-hB-DPP" firstAttribute="leading" secondItem="TKV-I9-3N8" secondAttribute="leading" id="ZI9-mi-dTu"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="lBO-a6-qnj" secondAttribute="trailing" constant="20" symbolic="YES" id="Ztx-Qh-mHp"/>
|
||||||
|
<constraint firstItem="lBO-a6-qnj" firstAttribute="top" secondItem="72F-hB-DPP" secondAttribute="bottom" constant="20" id="agT-yo-xZF"/>
|
||||||
|
<constraint firstItem="TKV-I9-3N8" firstAttribute="top" secondItem="WTb-Wq-BLo" secondAttribute="bottom" constant="8" symbolic="YES" id="kzd-et-rve"/>
|
||||||
|
<constraint firstItem="72F-hB-DPP" firstAttribute="trailing" secondItem="TKV-I9-3N8" secondAttribute="trailing" id="m9x-V7-HTG"/>
|
||||||
|
<constraint firstItem="2hB-Jy-QEw" firstAttribute="centerY" secondItem="lBO-a6-qnj" secondAttribute="centerY" id="mX6-db-4Tp"/>
|
||||||
|
<constraint firstItem="TKV-I9-3N8" firstAttribute="leading" secondItem="9w7-b6-jXh" secondAttribute="leading" id="pNd-TR-JzE"/>
|
||||||
|
<constraint firstItem="WTb-Wq-BLo" firstAttribute="top" secondItem="8QZ-37-H7U" secondAttribute="top" constant="20" symbolic="YES" id="q88-zV-efL"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="activityPurchase" destination="2hB-Jy-QEw" id="WcK-o8-BZZ"/>
|
||||||
|
<outlet property="buttonPurchase" destination="lBO-a6-qnj" id="m5t-5u-goQ"/>
|
||||||
|
<outlet property="buttonRestore" destination="XMI-x0-24k" id="Xhd-Ph-uUY"/>
|
||||||
|
<outlet property="labelFooter" destination="TKV-I9-3N8" id="DTN-XY-TSD"/>
|
||||||
|
<outlet property="labelRestore" destination="72F-hB-DPP" id="rnt-Zy-gEz"/>
|
||||||
|
<outlet property="tableView" destination="9w7-b6-jXh" id="0Ju-MH-sbN"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="GVf-vI-DWL" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="428" y="-9"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
|
@ -1220,7 +1220,7 @@ DQ
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="KOf-Ss-PtI" kind="sheet" id="131-KX-EPr"/>
|
<segue destination="KOf-Ss-PtI" kind="sheet" identifier="TrustedNetworkAddSegueIdentifier" id="131-KX-EPr"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="C9Y-vu-Z9f">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="C9Y-vu-Z9f">
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
import PassepartoutCore
|
||||||
|
|
||||||
class Macros {
|
class Macros {
|
||||||
static func warning(_ title: String, _ message: String) -> NSAlert {
|
static func warning(_ title: String, _ message: String) -> NSAlert {
|
||||||
|
@ -89,6 +90,15 @@ extension NSAlert {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension NSViewController {
|
||||||
|
func presentPurchaseScreen(forProduct product: Product, delegate: PurchaseViewControllerDelegate? = nil) {
|
||||||
|
let vc = StoryboardScene.Purchase.initialScene.instantiate()
|
||||||
|
vc.feature = product
|
||||||
|
vc.delegate = delegate
|
||||||
|
presentAsModalWindow(vc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension NSView {
|
extension NSView {
|
||||||
static func get<T: NSView>() -> T {
|
static func get<T: NSView>() -> T {
|
||||||
let name = String(describing: T.self)
|
let name = String(describing: T.self)
|
||||||
|
|
|
@ -24,6 +24,11 @@ internal enum StoryboardScene {
|
||||||
|
|
||||||
internal static let initialScene = InitialSceneType<PreferencesViewController>(storyboard: Preferences.self)
|
internal static let initialScene = InitialSceneType<PreferencesViewController>(storyboard: Preferences.self)
|
||||||
}
|
}
|
||||||
|
internal enum Purchase: StoryboardType {
|
||||||
|
internal static let storyboardName = "Purchase"
|
||||||
|
|
||||||
|
internal static let initialScene = InitialSceneType<PurchaseViewController>(storyboard: Purchase.self)
|
||||||
|
}
|
||||||
internal enum Service: StoryboardType {
|
internal enum Service: StoryboardType {
|
||||||
internal static let storyboardName = "Service"
|
internal static let storyboardName = "Service"
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ internal enum StoryboardSegue {
|
||||||
internal enum Service: String, SegueType {
|
internal enum Service: String, SegueType {
|
||||||
case accountSegueIdentifier = "AccountSegueIdentifier"
|
case accountSegueIdentifier = "AccountSegueIdentifier"
|
||||||
case customizeSegueIdentifier = "CustomizeSegueIdentifier"
|
case customizeSegueIdentifier = "CustomizeSegueIdentifier"
|
||||||
|
case trustedNetworkAddSegueIdentifier = "TrustedNetworkAddSegueIdentifier"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
|
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
|
||||||
|
|
|
@ -763,6 +763,30 @@ internal enum L10n {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
internal enum Purchase {
|
||||||
|
/// Purchase
|
||||||
|
internal static let title = L10n.tr("Core", "purchase.title")
|
||||||
|
internal enum Cells {
|
||||||
|
internal enum FullVersion {
|
||||||
|
/// - All providers (including future ones)\n%@
|
||||||
|
internal static func extraDescription(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Core", "purchase.cells.full_version.extra_description", String(describing: p1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal enum Restore {
|
||||||
|
/// If you bought this app or feature in the past, you can restore your purchases and this screen won't show again.
|
||||||
|
internal static let description = L10n.tr("Core", "purchase.cells.restore.description")
|
||||||
|
/// Restore purchases
|
||||||
|
internal static let title = L10n.tr("Core", "purchase.cells.restore.title")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal enum Sections {
|
||||||
|
internal enum Products {
|
||||||
|
/// Every product is a one-time purchase. Provider purchases do not include a VPN subscription.
|
||||||
|
internal static let footer = L10n.tr("Core", "purchase.sections.products.footer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
internal enum Reddit {
|
internal enum Reddit {
|
||||||
/// Did you know that Passepartout has a subreddit? Subscribe for updates or to discuss issues, features, new platforms or whatever you like.\n\nIt's also a great way to show you care about this project.
|
/// Did you know that Passepartout has a subreddit? Subscribe for updates or to discuss issues, features, new platforms or whatever you like.\n\nIt's also a great way to show you care about this project.
|
||||||
internal static let message = L10n.tr("Core", "reddit.message")
|
internal static let message = L10n.tr("Core", "reddit.message")
|
||||||
|
|
|
@ -131,28 +131,32 @@ class StatusMenu: NSObject {
|
||||||
|
|
||||||
let menuSupport = NSMenu()
|
let menuSupport = NSMenu()
|
||||||
let itemCommunity = NSMenuItem(title: L10n.Core.Organizer.Cells.JoinCommunity.caption.asContinuation, action: #selector(joinCommunity), keyEquivalent: "")
|
let itemCommunity = NSMenuItem(title: L10n.Core.Organizer.Cells.JoinCommunity.caption.asContinuation, action: #selector(joinCommunity), keyEquivalent: "")
|
||||||
let itemReview = NSMenuItem(title: L10n.Core.Organizer.Cells.WriteReview.caption.asContinuation, action: #selector(writeReview), keyEquivalent: "")
|
|
||||||
// let itemDonate = NSMenuItem(title: L10n.Core.Organizer.Cells.Donate.caption.asContinuation, action: #selector(showDonations), keyEquivalent: "")
|
// let itemDonate = NSMenuItem(title: L10n.Core.Organizer.Cells.Donate.caption.asContinuation, action: #selector(showDonations), keyEquivalent: "")
|
||||||
// let itemGitHubSponsors = NSMenuItem(title: L10n.Core.Organizer.Cells.GithubSponsors.caption.asContinuation, action: #selector(seeGitHubSponsors), keyEquivalent: "")
|
// let itemGitHubSponsors = NSMenuItem(title: L10n.Core.Organizer.Cells.GithubSponsors.caption.asContinuation, action: #selector(seeGitHubSponsors), keyEquivalent: "")
|
||||||
// let itemTranslate = NSMenuItem(title: L10n.Core.Organizer.Cells.Translate.caption.asContinuation, action: #selector(offerToTranslate), keyEquivalent: "")
|
// let itemTranslate = NSMenuItem(title: L10n.Core.Organizer.Cells.Translate.caption.asContinuation, action: #selector(offerToTranslate), keyEquivalent: "")
|
||||||
let itemFAQ = NSMenuItem(title: L10n.Core.About.Cells.Faq.caption.asContinuation, action: #selector(visitFAQ), keyEquivalent: "")
|
let itemFAQ = NSMenuItem(title: L10n.Core.About.Cells.Faq.caption.asContinuation, action: #selector(visitFAQ), keyEquivalent: "")
|
||||||
let itemReport = NSMenuItem(title: L10n.Core.Service.Cells.ReportIssue.caption.asContinuation, action: #selector(reportConnectivityIssue), keyEquivalent: "")
|
|
||||||
itemCommunity.target = self
|
itemCommunity.target = self
|
||||||
itemReview.target = self
|
|
||||||
// itemDonate.target = self
|
// itemDonate.target = self
|
||||||
// itemGitHubSponsors.target = self
|
// itemGitHubSponsors.target = self
|
||||||
// itemTranslate.target = self
|
// itemTranslate.target = self
|
||||||
itemFAQ.target = self
|
itemFAQ.target = self
|
||||||
itemReport.target = self
|
|
||||||
// menuSupport.addItem(itemDonate)
|
// menuSupport.addItem(itemDonate)
|
||||||
menuSupport.addItem(itemCommunity)
|
menuSupport.addItem(itemCommunity)
|
||||||
// menuSupport.addItem(.separator())
|
// menuSupport.addItem(.separator())
|
||||||
// menuSupport.addItem(itemGitHubSponsors)
|
// menuSupport.addItem(itemGitHubSponsors)
|
||||||
// menuSupport.addItem(itemTranslate)
|
// menuSupport.addItem(itemTranslate)
|
||||||
menuSupport.addItem(itemReview)
|
if ProductManager.shared.isEligibleForFeedback() {
|
||||||
|
let itemReview = NSMenuItem(title: L10n.Core.Organizer.Cells.WriteReview.caption.asContinuation, action: #selector(writeReview), keyEquivalent: "")
|
||||||
|
itemReview.target = self
|
||||||
|
menuSupport.addItem(itemReview)
|
||||||
|
}
|
||||||
menuSupport.addItem(.separator())
|
menuSupport.addItem(.separator())
|
||||||
menuSupport.addItem(itemFAQ)
|
menuSupport.addItem(itemFAQ)
|
||||||
menuSupport.addItem(itemReport)
|
if ProductManager.shared.isEligibleForFeedback() {
|
||||||
|
let itemReport = NSMenuItem(title: L10n.Core.Service.Cells.ReportIssue.caption.asContinuation, action: #selector(reportConnectivityIssue), keyEquivalent: "")
|
||||||
|
itemReport.target = self
|
||||||
|
menuSupport.addItem(itemReport)
|
||||||
|
}
|
||||||
let itemSupport = NSMenuItem(title: L10n.App.Menu.Support.title, action: nil, keyEquivalent: "")
|
let itemSupport = NSMenuItem(title: L10n.App.Menu.Support.title, action: nil, keyEquivalent: "")
|
||||||
menu.setSubmenu(menuSupport, for: itemSupport)
|
menu.setSubmenu(menuSupport, for: itemSupport)
|
||||||
menu.addItem(itemSupport)
|
menu.addItem(itemSupport)
|
||||||
|
|
|
@ -88,6 +88,12 @@ class OrganizerViewController: NSViewController {
|
||||||
guard let item = sender as? NSMenuItem, let metadata = item.representedObject as? Infrastructure.Metadata else {
|
guard let item = sender as? NSMenuItem, let metadata = item.representedObject as? Infrastructure.Metadata else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
do {
|
||||||
|
try ProductManager.shared.verifyEligible(forProvider: metadata)
|
||||||
|
} catch {
|
||||||
|
presentPurchaseScreen(forProduct: metadata.product)
|
||||||
|
return
|
||||||
|
}
|
||||||
perform(segue: StoryboardSegue.Main.enterAccountSegueIdentifier, sender: metadata.name)
|
perform(segue: StoryboardSegue.Main.enterAccountSegueIdentifier, sender: metadata.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
//
|
||||||
|
// PurchaseProductView.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 2/4/21.
|
||||||
|
// Copyright (c) 2021 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of Passepartout.
|
||||||
|
//
|
||||||
|
// Passepartout is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Passepartout is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import StoreKit
|
||||||
|
|
||||||
|
class PurchaseProductView: NSView {
|
||||||
|
@IBOutlet private weak var labelTitle: NSTextField?
|
||||||
|
|
||||||
|
@IBOutlet private weak var labelPrice: NSTextField?
|
||||||
|
|
||||||
|
@IBOutlet private weak var labelDescription: NSTextField?
|
||||||
|
|
||||||
|
func fill(product: SKProduct, customDescription: String? = nil) {
|
||||||
|
fill(
|
||||||
|
title: product.localizedTitle,
|
||||||
|
description: customDescription ?? "\(product.localizedDescription)."
|
||||||
|
)
|
||||||
|
labelPrice?.stringValue = product.localizedPrice ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func fill(title: String, description: String) {
|
||||||
|
labelTitle?.stringValue = title
|
||||||
|
labelDescription?.stringValue = description
|
||||||
|
labelPrice?.stringValue = ""
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,274 @@
|
||||||
|
//
|
||||||
|
// PurchaseViewController.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 2/2/21.
|
||||||
|
// Copyright (c) 2021 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of Passepartout.
|
||||||
|
//
|
||||||
|
// Passepartout is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Passepartout is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import StoreKit
|
||||||
|
import PassepartoutCore
|
||||||
|
import SwiftyBeaver
|
||||||
|
import Convenience
|
||||||
|
|
||||||
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
|
protocol PurchaseViewControllerDelegate: class {
|
||||||
|
func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product)
|
||||||
|
}
|
||||||
|
|
||||||
|
class PurchaseViewController: NSViewController {
|
||||||
|
private struct Columns {
|
||||||
|
static let product = NSUserInterfaceItemIdentifier("ProductCellIdentifier")
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBOutlet private weak var tableView: NSTableView!
|
||||||
|
|
||||||
|
@IBOutlet private weak var labelFooter: NSTextField!
|
||||||
|
|
||||||
|
@IBOutlet private weak var labelRestore: NSTextField!
|
||||||
|
|
||||||
|
@IBOutlet private weak var activityPurchase: NSProgressIndicator!
|
||||||
|
|
||||||
|
@IBOutlet private weak var buttonPurchase: NSButton!
|
||||||
|
|
||||||
|
@IBOutlet private weak var buttonRestore: NSButton!
|
||||||
|
|
||||||
|
var feature: Product!
|
||||||
|
|
||||||
|
weak var delegate: PurchaseViewControllerDelegate?
|
||||||
|
|
||||||
|
private var skFeature: SKProduct?
|
||||||
|
|
||||||
|
private var skPlatformVersion: SKProduct?
|
||||||
|
|
||||||
|
private var skFullVersion: SKProduct?
|
||||||
|
|
||||||
|
private var platformVersionExtra: String?
|
||||||
|
|
||||||
|
private var fullVersionExtra: String?
|
||||||
|
|
||||||
|
var rows: [RowType] = []
|
||||||
|
|
||||||
|
func reloadModel() {
|
||||||
|
rows = []
|
||||||
|
let pm = ProductManager.shared
|
||||||
|
if let skPlatformVersion = pm.product(withIdentifier: .fullVersion_macOS) {
|
||||||
|
self.skPlatformVersion = skPlatformVersion
|
||||||
|
rows.append(.platformVersion)
|
||||||
|
}
|
||||||
|
if let skFullVersion = pm.product(withIdentifier: .fullVersion) {
|
||||||
|
self.skFullVersion = skFullVersion
|
||||||
|
rows.append(.fullVersion)
|
||||||
|
}
|
||||||
|
if let skFeature = pm.product(withIdentifier: feature) {
|
||||||
|
self.skFeature = skFeature
|
||||||
|
rows.append(.feature)
|
||||||
|
}
|
||||||
|
|
||||||
|
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_macOS]).map {
|
||||||
|
return $0.localizedTitle
|
||||||
|
}.sortedCaseInsensitive()
|
||||||
|
let fullBullets = fullBulletsList.joined(separator: "\n")
|
||||||
|
fullVersionExtra = L10n.Core.Purchase.Cells.FullVersion.extraDescription(fullBullets)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: NSViewController
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
title = L10n.Core.Purchase.title
|
||||||
|
labelFooter.stringValue = L10n.Core.Purchase.Sections.Products.footer
|
||||||
|
labelRestore.stringValue = L10n.Core.Purchase.Cells.Restore.description
|
||||||
|
buttonPurchase.title = L10n.Core.Purchase.title
|
||||||
|
buttonRestore.title = L10n.Core.Purchase.Cells.Restore.title
|
||||||
|
|
||||||
|
guard let _ = feature else {
|
||||||
|
fatalError("No feature set for purchase")
|
||||||
|
}
|
||||||
|
|
||||||
|
tableView.usesAutomaticRowHeights = true
|
||||||
|
tableView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear() {
|
||||||
|
super.viewWillAppear()
|
||||||
|
|
||||||
|
view.window?.styleMask = [.closable, .titled]
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear() {
|
||||||
|
super.viewDidAppear()
|
||||||
|
|
||||||
|
startWaiting()
|
||||||
|
ProductManager.shared.listProducts { [weak self] (_, _) in
|
||||||
|
self?.reloadModel()
|
||||||
|
self?.tableView.reloadData()
|
||||||
|
self?.stopWaiting()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Actions
|
||||||
|
|
||||||
|
@IBAction private func doPurchase(_ sender: Any) {
|
||||||
|
guard tableView.selectedRow != -1 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch rows[tableView.selectedRow] {
|
||||||
|
case .feature:
|
||||||
|
purchaseFeature()
|
||||||
|
|
||||||
|
case .platformVersion:
|
||||||
|
purchasePlatformVersion()
|
||||||
|
|
||||||
|
case .fullVersion:
|
||||||
|
purchaseFullVersion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction private func doRestorePurchases(_ sender: Any) {
|
||||||
|
startWaiting()
|
||||||
|
ProductManager.shared.restorePurchases { [weak self] in
|
||||||
|
self?.stopWaiting()
|
||||||
|
guard $0 == nil else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self?.dismiss(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func purchaseFeature() {
|
||||||
|
guard let sk = skFeature else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
purchase(sk)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func purchasePlatformVersion() {
|
||||||
|
guard let sk = skPlatformVersion else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
purchase(sk)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func purchaseFullVersion() {
|
||||||
|
guard let sk = skFullVersion else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
purchase(sk)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func purchase(_ skProduct: SKProduct) {
|
||||||
|
startWaiting()
|
||||||
|
ProductManager.shared.purchase(skProduct) { [weak self] in
|
||||||
|
self?.stopWaiting()
|
||||||
|
guard $0 == .success else {
|
||||||
|
if let error = $1 {
|
||||||
|
self?.reportPurchaseError(withProduct: skProduct, error: error)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let weakSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let product = weakSelf.feature.matchesStoreKitProduct(skProduct) ? weakSelf.feature! : .fullVersion
|
||||||
|
weakSelf.delegate?.purchaseController(weakSelf, didPurchase: product)
|
||||||
|
|
||||||
|
self?.dismiss(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func reportPurchaseError(withProduct product: SKProduct, error: Error) {
|
||||||
|
log.error("Unable to purchase \(product): \(error)")
|
||||||
|
|
||||||
|
let alert = Macros.warning(product.localizedTitle, error.localizedDescription)
|
||||||
|
_ = alert.presentModally(withOK: L10n.Core.Global.ok, cancel: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func close() {
|
||||||
|
dismiss(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Helpers
|
||||||
|
|
||||||
|
private func startWaiting() {
|
||||||
|
tableView.isEnabled = false
|
||||||
|
buttonPurchase.isEnabled = false
|
||||||
|
buttonRestore.isEnabled = false
|
||||||
|
activityPurchase.isHidden = false
|
||||||
|
activityPurchase.startAnimation(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stopWaiting() {
|
||||||
|
activityPurchase.stopAnimation(nil)
|
||||||
|
tableView.isEnabled = true
|
||||||
|
buttonPurchase.isEnabled = true
|
||||||
|
buttonRestore.isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PurchaseViewController: NSTableViewDataSource, NSTableViewDelegate {
|
||||||
|
enum RowType {
|
||||||
|
case feature
|
||||||
|
|
||||||
|
case platformVersion
|
||||||
|
|
||||||
|
case fullVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||||
|
return rows.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||||
|
guard let view = tableView.makeView(withIdentifier: Columns.product, owner: nil) as? PurchaseProductView else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch rows[row] {
|
||||||
|
case .feature:
|
||||||
|
guard let product = skFeature else {
|
||||||
|
fatalError("Loaded feature cell, yet no corresponding product?")
|
||||||
|
}
|
||||||
|
view.fill(product: product)
|
||||||
|
|
||||||
|
case .platformVersion:
|
||||||
|
guard let product = skPlatformVersion else {
|
||||||
|
fatalError("Loaded platform version cell, yet no corresponding product?")
|
||||||
|
}
|
||||||
|
view.fill(product: product, customDescription: platformVersionExtra)
|
||||||
|
|
||||||
|
case .fullVersion:
|
||||||
|
guard let product = skFullVersion else {
|
||||||
|
fatalError("Loaded full version cell, yet no corresponding product?")
|
||||||
|
}
|
||||||
|
view.fill(product: product, customDescription: fullVersionExtra)
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
}
|
|
@ -107,6 +107,13 @@ class TrustedNetworksViewController: NSViewController, ProfileCustomization {
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction private func toggleTrustEthernet(_ sender: Any?) {
|
@IBAction private func toggleTrustEthernet(_ sender: Any?) {
|
||||||
|
do {
|
||||||
|
try ProductManager.shared.verifyEligibleForTrustedNetworks()
|
||||||
|
} catch {
|
||||||
|
checkTrustEthernet.state = .off
|
||||||
|
presentPurchaseScreen(forProduct: .fullVersion_macOS)
|
||||||
|
return
|
||||||
|
}
|
||||||
trustedNetworks.includesEthernet = (checkTrustEthernet.state == .on)
|
trustedNetworks.includesEthernet = (checkTrustEthernet.state == .on)
|
||||||
|
|
||||||
delegate?.profileCustomization(self, didUpdateTrustedNetworks: trustedNetworks)
|
delegate?.profileCustomization(self, didUpdateTrustedNetworks: trustedNetworks)
|
||||||
|
@ -122,6 +129,18 @@ class TrustedNetworksViewController: NSViewController, ProfileCustomization {
|
||||||
delegate?.profileCustomization(self, didUpdateTrustedNetworks: trustedNetworks)
|
delegate?.profileCustomization(self, didUpdateTrustedNetworks: trustedNetworks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func shouldPerformSegue(withIdentifier identifier: NSStoryboardSegue.Identifier, sender: Any?) -> Bool {
|
||||||
|
if identifier == StoryboardSegue.Service.trustedNetworkAddSegueIdentifier.rawValue {
|
||||||
|
do {
|
||||||
|
try ProductManager.shared.verifyEligibleForTrustedNetworks()
|
||||||
|
} catch {
|
||||||
|
presentPurchaseScreen(forProduct: .fullVersion_macOS)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
|
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
|
||||||
if let addVC = segue.destinationController as? TrustedNetworksAddViewController {
|
if let addVC = segue.destinationController as? TrustedNetworksAddViewController {
|
||||||
addVC.delegate = self
|
addVC.delegate = self
|
||||||
|
|
|
@ -8,9 +8,9 @@ strings:
|
||||||
|
|
||||||
ib:
|
ib:
|
||||||
inputs:
|
inputs:
|
||||||
#- Base.lproj/About.storyboard
|
|
||||||
- Base.lproj/Main.storyboard
|
- Base.lproj/Main.storyboard
|
||||||
- Base.lproj/Preferences.storyboard
|
- Base.lproj/Preferences.storyboard
|
||||||
|
- Base.lproj/Purchase.storyboard
|
||||||
- Base.lproj/Service.storyboard
|
- Base.lproj/Service.storyboard
|
||||||
#- Base.lproj/Shortcuts.storyboard
|
#- Base.lproj/Shortcuts.storyboard
|
||||||
outputs:
|
outputs:
|
||||||
|
|
|
@ -240,6 +240,12 @@
|
||||||
|
|
||||||
"shortcuts.edit.sections.all.header" = "Existierende Kurzbefehle";
|
"shortcuts.edit.sections.all.header" = "Existierende Kurzbefehle";
|
||||||
|
|
||||||
|
"purchase.title" = "Kaufen";
|
||||||
|
"purchase.sections.products.footer" = "Jedes Produkt ist ein einmaliger Kauf. Der Kauf eines Providers beinhaltet kein VPN-Abonnement.";
|
||||||
|
"purchase.cells.full_version.extra_description" = "Alle Anbieter (inklusive Zukünftige)\n%@";
|
||||||
|
"purchase.cells.restore.title" = "Einkäufe wiederherstellen";
|
||||||
|
"purchase.cells.restore.description" = "Wenn Sie diese App oder Funktion in der Vergangenheit gekauft haben, können Sie Ihre Einkäufe wiederherstellen und dieser Bildschirm wird nicht mehr angezeigt.";
|
||||||
|
|
||||||
"donation.title" = "Spenden";
|
"donation.title" = "Spenden";
|
||||||
"donation.sections.one_time.header" = "Einmalig";
|
"donation.sections.one_time.header" = "Einmalig";
|
||||||
"donation.sections.one_time.footer" = "Wenn du dich erkenntlich zeigen möchtest für meine Arbeit, gibt es hier ein paar Beträge die du direkt spenden kannst.\n\nDu bezahlst pro Spende nur einmal und kannst mehrmals spenden wenn du möchtest.";
|
"donation.sections.one_time.footer" = "Wenn du dich erkenntlich zeigen möchtest für meine Arbeit, gibt es hier ein paar Beträge die du direkt spenden kannst.\n\nDu bezahlst pro Spende nur einmal und kannst mehrmals spenden wenn du möchtest.";
|
||||||
|
|
|
@ -240,6 +240,12 @@
|
||||||
|
|
||||||
"shortcuts.edit.sections.all.header" = "Υπάρχουσες συντομεύσεις";
|
"shortcuts.edit.sections.all.header" = "Υπάρχουσες συντομεύσεις";
|
||||||
|
|
||||||
|
"purchase.title" = "Αγορά";
|
||||||
|
"purchase.sections.products.footer" = "Κάθε προϊόν είναι μια αγορά. Οι αγορές παρόχων δεν περιλαμβάνουν τη συνδρομή VPN.";
|
||||||
|
"purchase.cells.full_version.extra_description" = "Όλοι οι πάροχοι (περιλαμβάνονται και οι μελλοντικοί)\n%@";
|
||||||
|
"purchase.cells.restore.title" = "Επαναφορά Αγορών";
|
||||||
|
"purchase.cells.restore.description" = "Εαν αγοράσατε την εφαρμογή στο παρελθόν, μπορείτε να κάνετε επαναφορά αγορών και αυτή η οθόνη δε θα εμφανιστεί ξανά.";
|
||||||
|
|
||||||
"donation.title" = "Δωρεά";
|
"donation.title" = "Δωρεά";
|
||||||
"donation.sections.one_time.header" = "Μια Φορά";
|
"donation.sections.one_time.header" = "Μια Φορά";
|
||||||
"donation.sections.one_time.footer" = "Αν είστε χαρούμενη με τη δουλειά μου, εδώ είναι λίγα ποσά που μπορείτε να δώσετε αμέσως.\n\nΘα χρεωθείτε μόνο μία φορά και μπορείτε να δώσετε πολλές φορές.";
|
"donation.sections.one_time.footer" = "Αν είστε χαρούμενη με τη δουλειά μου, εδώ είναι λίγα ποσά που μπορείτε να δώσετε αμέσως.\n\nΘα χρεωθείτε μόνο μία φορά και μπορείτε να δώσετε πολλές φορές.";
|
||||||
|
|
|
@ -240,6 +240,12 @@
|
||||||
|
|
||||||
"shortcuts.edit.sections.all.header" = "Existing shortcuts";
|
"shortcuts.edit.sections.all.header" = "Existing shortcuts";
|
||||||
|
|
||||||
|
"purchase.title" = "Purchase";
|
||||||
|
"purchase.sections.products.footer" = "Every product is a one-time purchase. Provider purchases do not include a VPN subscription.";
|
||||||
|
"purchase.cells.full_version.extra_description" = "All providers (including future ones)\n%@";
|
||||||
|
"purchase.cells.restore.title" = "Restore purchases";
|
||||||
|
"purchase.cells.restore.description" = "If you bought this app or feature in the past, you can restore your purchases and this screen won't show again.";
|
||||||
|
|
||||||
"donation.title" = "Donate";
|
"donation.title" = "Donate";
|
||||||
"donation.sections.one_time.header" = "One time";
|
"donation.sections.one_time.header" = "One time";
|
||||||
"donation.sections.one_time.footer" = "If you want to display gratitude for my free work, here are a couple amounts you can donate instantly.\n\nYou will only be charged once per donation, and you can donate multiple times.";
|
"donation.sections.one_time.footer" = "If you want to display gratitude for my free work, here are a couple amounts you can donate instantly.\n\nYou will only be charged once per donation, and you can donate multiple times.";
|
||||||
|
|
|
@ -240,6 +240,12 @@
|
||||||
|
|
||||||
"shortcuts.edit.sections.all.header" = "Atajos existentes";
|
"shortcuts.edit.sections.all.header" = "Atajos existentes";
|
||||||
|
|
||||||
|
"purchase.title" = "Compra";
|
||||||
|
"purchase.sections.products.footer" = "Cada producto es una compra única y no recurrente. La compra de un proveedor no incluye una suscripción al servicio.";
|
||||||
|
"purchase.cells.full_version.extra_description" = "Todos los proveedores (incluye los futuros)\n%@";
|
||||||
|
"purchase.cells.restore.title" = "Restaurar compras";
|
||||||
|
"purchase.cells.restore.description" = "Si compraste esta aplicación o funcionalidad anteriormente, puedes restaurar tus compras y esta pantalla no volverá a aparecer.";
|
||||||
|
|
||||||
"donation.title" = "Donar";
|
"donation.title" = "Donar";
|
||||||
"donation.sections.one_time.header" = "Única";
|
"donation.sections.one_time.header" = "Única";
|
||||||
"donation.sections.one_time.footer" = "Si te gusta mi trabajo, aquí puedes colaborar con una donación.\n\nSólo se te cobrará una vez por donación, y puedes donar las veces que quieras.";
|
"donation.sections.one_time.footer" = "Si te gusta mi trabajo, aquí puedes colaborar con una donación.\n\nSólo se te cobrará una vez por donación, y puedes donar las veces que quieras.";
|
||||||
|
|
|
@ -240,6 +240,12 @@
|
||||||
|
|
||||||
"shortcuts.edit.sections.all.header" = "Raccourcis existants";
|
"shortcuts.edit.sections.all.header" = "Raccourcis existants";
|
||||||
|
|
||||||
|
"purchase.title" = "Acheter";
|
||||||
|
"purchase.sections.products.footer" = "Chaque produit est un achat unique. Les achats n'incluent pas une souscription à un service de VPN.";
|
||||||
|
"purchase.cells.full_version.extra_description" = "Tous les fournisseurs (incluant les prochains)\n%@";
|
||||||
|
"purchase.cells.restore.title" = "Restaurer les achats";
|
||||||
|
"purchase.cells.restore.description" = "Si vous avez acheté l'application ou une fonctionnalité dans le passé, vous pouvez restaurer les achats et ce message ne s'affichera plus.";
|
||||||
|
|
||||||
"donation.title" = "Faire un don";
|
"donation.title" = "Faire un don";
|
||||||
"donation.sections.one_time.header" = "Une seule fois";
|
"donation.sections.one_time.header" = "Une seule fois";
|
||||||
"donation.sections.one_time.footer" = "Si vous voulez manifester votre gratitude envers mon travail bénévole, voici certains montants pour faire un don instantanément.\n\n Vous n'allez être chargé qu'une seule fois par don et vous pouvez faire un don plus d'une fois.";
|
"donation.sections.one_time.footer" = "Si vous voulez manifester votre gratitude envers mon travail bénévole, voici certains montants pour faire un don instantanément.\n\n Vous n'allez être chargé qu'une seule fois par don et vous pouvez faire un don plus d'une fois.";
|
||||||
|
|
|
@ -240,6 +240,12 @@
|
||||||
|
|
||||||
"shortcuts.edit.sections.all.header" = "Comandi esistenti";
|
"shortcuts.edit.sections.all.header" = "Comandi esistenti";
|
||||||
|
|
||||||
|
"purchase.title" = "Acquista";
|
||||||
|
"purchase.sections.products.footer" = "Ogni prodotto è un acquisto unico e non ricorrente. L'acquisto di un provider non include una sottoscrizione.";
|
||||||
|
"purchase.cells.full_version.extra_description" = "Tutti i provider (inclusi quelli futuri)\n%@";
|
||||||
|
"purchase.cells.restore.title" = "Ripristina acquisti";
|
||||||
|
"purchase.cells.restore.description" = "Se hai comprato quest'applicazione o funzionalità in precedenza, puoi ripristinare i tuoi acquisti in modo che questa schermata non compaia più.";
|
||||||
|
|
||||||
"donation.title" = "Donazione";
|
"donation.title" = "Donazione";
|
||||||
"donation.sections.one_time.header" = "Unica";
|
"donation.sections.one_time.header" = "Unica";
|
||||||
"donation.sections.one_time.footer" = "Se vuoi mostrare gratitudine per il mio lavoro a titolo gratuito, qui trovi varie somme da donare all'istante.\n\nLa donazione ti sarà addebitata solo una volta, e puoi effettuare più donazioni.";
|
"donation.sections.one_time.footer" = "Se vuoi mostrare gratitudine per il mio lavoro a titolo gratuito, qui trovi varie somme da donare all'istante.\n\nLa donazione ti sarà addebitata solo una volta, e puoi effettuare più donazioni.";
|
||||||
|
|
|
@ -240,6 +240,12 @@
|
||||||
|
|
||||||
"shortcuts.edit.sections.all.header" = "Bestaande snelkoppelingen";
|
"shortcuts.edit.sections.all.header" = "Bestaande snelkoppelingen";
|
||||||
|
|
||||||
|
"purchase.title" = "Aanschaffen";
|
||||||
|
"purchase.sections.products.footer" = "Elk product is een eenmalige aankoop. Aankopen van providers bevatten geen VPN-abonnement.";
|
||||||
|
"purchase.cells.full_version.extra_description" = "Alle providers (inclusief toekomstige)\n%@";
|
||||||
|
"purchase.cells.restore.title" = "Herstel Aankopen";
|
||||||
|
"purchase.cells.restore.description" = "Als u deze app of functie in het verleden heeft gekocht, kunt u uw aankopen herstellen en wordt dit scherm niet meer getoond.";
|
||||||
|
|
||||||
"donation.title" = "Donatie";
|
"donation.title" = "Donatie";
|
||||||
"donation.sections.one_time.header" = "Eenmalig";
|
"donation.sections.one_time.header" = "Eenmalig";
|
||||||
"donation.sections.one_time.footer" = "Als je dankbaarheid wilt tonen voor mijn gratis werk, zijn hier een paar bedragen die je direct kunt doneren.\n\nHet bedrag wordt slechts één keer per donatie in rekening gebracht en u kunt meerdere keren doneren.";
|
"donation.sections.one_time.footer" = "Als je dankbaarheid wilt tonen voor mijn gratis werk, zijn hier een paar bedragen die je direct kunt doneren.\n\nHet bedrag wordt slechts één keer per donatie in rekening gebracht en u kunt meerdere keren doneren.";
|
||||||
|
|
|
@ -240,6 +240,12 @@
|
||||||
|
|
||||||
"shortcuts.edit.sections.all.header" = "Istniejące skróty";
|
"shortcuts.edit.sections.all.header" = "Istniejące skróty";
|
||||||
|
|
||||||
|
"purchase.title" = "Kup";
|
||||||
|
"purchase.sections.products.footer" = "Każdy produkt to zakup jednorazowy. Kuipno usługodawcy nie zawiera subskrypcji VPN.";
|
||||||
|
"purchase.cells.full_version.extra_description" = "Wszyscy usługodawcy (włączając przyszłych)\n%@";
|
||||||
|
"purchase.cells.restore.title" = "Przywróć zakup";
|
||||||
|
"purchase.cells.restore.description" = "Jeśli kupiłeś tą aplikację lub funkcję wcześniej, możesz przywrócić swoje zakupy i ten ekran nie będzie wyświetlony ponownie.";
|
||||||
|
|
||||||
"donation.title" = "Dotacja";
|
"donation.title" = "Dotacja";
|
||||||
"donation.sections.one_time.header" = "Jeden raz";
|
"donation.sections.one_time.header" = "Jeden raz";
|
||||||
"donation.sections.one_time.footer" = "Jeśli chcesz docenić moją pracę, poniżej znajdziesz kilka kwot do wyboru dotacji.\n\nTwoje konto zostanie obciążone tylko raz na jedną dotację, możesz wysłać dotację kilka razy.";
|
"donation.sections.one_time.footer" = "Jeśli chcesz docenić moją pracę, poniżej znajdziesz kilka kwot do wyboru dotacji.\n\nTwoje konto zostanie obciążone tylko raz na jedną dotację, możesz wysłać dotację kilka razy.";
|
||||||
|
|
|
@ -240,6 +240,12 @@
|
||||||
|
|
||||||
"shortcuts.edit.sections.all.header" = "Atalhos existentes";
|
"shortcuts.edit.sections.all.header" = "Atalhos existentes";
|
||||||
|
|
||||||
|
"purchase.title" = "Comprar";
|
||||||
|
"purchase.sections.products.footer" = "Todo produto é uma compra única. As compras do fornecedor não incluem uma assinatura VPN.";
|
||||||
|
"purchase.cells.full_version.extra_description" = "Todos os provedores (incluindo os futuros)\n%@";
|
||||||
|
"purchase.cells.restore.title" = "Restaurar compras";
|
||||||
|
"purchase.cells.restore.description" = "Se você comprou este aplicativo ou recurso no passado, pode restaurar suas compras e essa tela não será exibida novamente.";
|
||||||
|
|
||||||
"donation.title" = "Doar";
|
"donation.title" = "Doar";
|
||||||
"donation.sections.one_time.header" = "Uma vez";
|
"donation.sections.one_time.header" = "Uma vez";
|
||||||
"donation.sections.one_time.footer" = "Se você deseja mostrar gratidão pelo meu trabalho, aqui estão alguns valores do qual você pode contribuir.\n\nVocé só será cobrado uma única vez, ou doar mais vezes caso desejar.";
|
"donation.sections.one_time.footer" = "Se você deseja mostrar gratidão pelo meu trabalho, aqui estão alguns valores do qual você pode contribuir.\n\nVocé só será cobrado uma única vez, ou doar mais vezes caso desejar.";
|
||||||
|
|
|
@ -240,6 +240,12 @@
|
||||||
|
|
||||||
"shortcuts.edit.sections.all.header" = "Существующие команды";
|
"shortcuts.edit.sections.all.header" = "Существующие команды";
|
||||||
|
|
||||||
|
"purchase.title" = "Покупка";
|
||||||
|
"purchase.sections.products.footer" = "Каждый продукт является разовой покупкой. Покупка провайдера не включает подписку на VPN.";
|
||||||
|
"purchase.cells.full_version.extra_description" = "Все провайдеры (включая добавленных в будущем)\n%@";
|
||||||
|
"purchase.cells.restore.title" = "Восстановить покупки";
|
||||||
|
"purchase.cells.restore.description" = "Если Вы купили это приложение или совершили встроенные покупки в прошлом, вы можете восстановить ваши покупки, и этот баннер больше не появится.";
|
||||||
|
|
||||||
"donation.title" = "Пожертвовать";
|
"donation.title" = "Пожертвовать";
|
||||||
"donation.sections.one_time.header" = "Один раз";
|
"donation.sections.one_time.header" = "Один раз";
|
||||||
"donation.sections.one_time.footer" = "Если Вы хотите поблагодарить мою бесплатную работу, здесь есть несколько сумм, которые Вы можете пожертвовать прямо сейчас.\n\nСумма будет списана только один раз, а Вы можете пожертвовать несколько раз.";
|
"donation.sections.one_time.footer" = "Если Вы хотите поблагодарить мою бесплатную работу, здесь есть несколько сумм, которые Вы можете пожертвовать прямо сейчас.\n\nСумма будет списана только один раз, а Вы можете пожертвовать несколько раз.";
|
||||||
|
|
|
@ -240,6 +240,12 @@
|
||||||
|
|
||||||
"shortcuts.edit.sections.all.header" = "Befintliga genvägar";
|
"shortcuts.edit.sections.all.header" = "Befintliga genvägar";
|
||||||
|
|
||||||
|
"purchase.title" = "Köp";
|
||||||
|
"purchase.sections.products.footer" = "Varje produkt är ett engångsköp. Leverantörsköp inkluderar inte ett VPN-abonnemang.";
|
||||||
|
"purchase.cells.full_version.extra_description" = "Alla leverantörer (inklusive framtida)\n%@";
|
||||||
|
"purchase.cells.restore.title" = "Återställ köp";
|
||||||
|
"purchase.cells.restore.description" = "Om du köpte den här appen eller funktionen tidigare kan du återställa dina inköp och den här skärmen visas inte igen.";
|
||||||
|
|
||||||
"donation.title" = "Donera";
|
"donation.title" = "Donera";
|
||||||
"donation.sections.one_time.header" = "En gång";
|
"donation.sections.one_time.header" = "En gång";
|
||||||
"donation.sections.one_time.footer" = "Om du vill visa tacksamhet för mitt fria arbete, här är några belopp du kan donera direkt. \n\nDu betalas endast en gång per donation, och du kan donera flera gånger. ";
|
"donation.sections.one_time.footer" = "Om du vill visa tacksamhet för mitt fria arbete, här är några belopp du kan donera direkt. \n\nDu betalas endast en gång per donation, och du kan donera flera gånger. ";
|
||||||
|
|
|
@ -240,6 +240,12 @@
|
||||||
|
|
||||||
"shortcuts.edit.sections.all.header" = "已经存在的捷径";
|
"shortcuts.edit.sections.all.header" = "已经存在的捷径";
|
||||||
|
|
||||||
|
"purchase.title" = "购买";
|
||||||
|
"purchase.sections.products.footer" = "每件产品都是一次性的购买。 购买的提供商并不包含VPN订阅。";
|
||||||
|
"purchase.cells.full_version.extra_description" = "所有的提供商(包括未来添加的)\n%@";
|
||||||
|
"purchase.cells.restore.title" = "恢复购买";
|
||||||
|
"purchase.cells.restore.description" = "如果你购买过此应用或其特征, 你可以恢复购买,此页面将不在显示。";
|
||||||
|
|
||||||
"donation.title" = "捐助";
|
"donation.title" = "捐助";
|
||||||
"donation.sections.one_time.header" = "一次性";
|
"donation.sections.one_time.header" = "一次性";
|
||||||
"donation.sections.one_time.footer" = "如果你想对我免费的工作表示感谢,这里是一些你可以捐助的数额。\n\n每次捐助只需要付款一次,你可以捐助多次。";
|
"donation.sections.one_time.footer" = "如果你想对我免费的工作表示感谢,这里是一些你可以捐助的数额。\n\n每次捐助只需要付款一次,你可以捐助多次。";
|
||||||
|
|
|
@ -359,12 +359,7 @@ public class AppConstants {
|
||||||
|
|
||||||
static let lastFullVersionBuild = 2016
|
static let lastFullVersionBuild = 2016
|
||||||
#else
|
#else
|
||||||
static var isBetaFullVersion: Bool {
|
static let isBetaFullVersion = false
|
||||||
guard !ProcessInfo.processInfo.arguments.contains("FULL_VERSION") else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
static let lastFullVersionBuild = 0
|
static let lastFullVersionBuild = 0
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -553,25 +553,29 @@ public class ConnectionService: Codable {
|
||||||
log.verbose(protocolConfiguration)
|
log.verbose(protocolConfiguration)
|
||||||
|
|
||||||
var rules: [NEOnDemandRule] = []
|
var rules: [NEOnDemandRule] = []
|
||||||
#if os(iOS)
|
do {
|
||||||
if profile.trustedNetworks.includesMobile {
|
try ProductManager.shared.verifyEligibleForTrustedNetworks()
|
||||||
let rule = policyRule(for: profile)
|
#if os(iOS)
|
||||||
rule.interfaceTypeMatch = .cellular
|
if profile.trustedNetworks.includesMobile {
|
||||||
rules.append(rule)
|
let rule = policyRule(for: profile)
|
||||||
}
|
rule.interfaceTypeMatch = .cellular
|
||||||
#else
|
rules.append(rule)
|
||||||
if profile.trustedNetworks.includesEthernet {
|
}
|
||||||
let rule = policyRule(for: profile)
|
#else
|
||||||
rule.interfaceTypeMatch = .ethernet
|
if profile.trustedNetworks.includesEthernet {
|
||||||
rules.append(rule)
|
let rule = policyRule(for: profile)
|
||||||
}
|
rule.interfaceTypeMatch = .ethernet
|
||||||
#endif
|
rules.append(rule)
|
||||||
let reallyTrustedWifis = Array(profile.trustedNetworks.includedWiFis.filter { $1 }.keys)
|
}
|
||||||
if !reallyTrustedWifis.isEmpty {
|
#endif
|
||||||
let rule = policyRule(for: profile)
|
let reallyTrustedWifis = Array(profile.trustedNetworks.includedWiFis.filter { $1 }.keys)
|
||||||
rule.interfaceTypeMatch = .wiFi
|
if !reallyTrustedWifis.isEmpty {
|
||||||
rule.ssidMatch = reallyTrustedWifis
|
let rule = policyRule(for: profile)
|
||||||
rules.append(rule)
|
rule.interfaceTypeMatch = .wiFi
|
||||||
|
rule.ssidMatch = reallyTrustedWifis
|
||||||
|
rules.append(rule)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
}
|
}
|
||||||
let connection = NEOnDemandRuleConnect()
|
let connection = NEOnDemandRuleConnect()
|
||||||
connection.interfaceTypeMatch = .any
|
connection.interfaceTypeMatch = .any
|
||||||
|
|
Loading…
Reference in New Issue