From 97f458385ce86b2e448b1c320986a910cda8a292 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Sat, 23 Nov 2019 14:22:34 +0100 Subject: [PATCH 1/7] Update to API v3 - Dynamic providers - Add index.json to API --- Passepartout-iOS/AppDelegate.swift | 2 +- Passepartout-iOS/Global/IssueReporter.swift | 4 +-- Passepartout-iOS/Global/Product.swift | 6 ++-- Passepartout-iOS/Global/ProductManager.swift | 2 +- Passepartout-iOS/Global/Theme.swift | 5 ++-- .../Scenes/AccountViewController.swift | 28 +++++++++++-------- .../Organizer/OrganizerViewController.swift | 17 +++++------ .../WizardProviderViewController.swift | 24 ++++++++-------- .../Scenes/ServiceViewController.swift | 7 ++--- Passepartout.xcodeproj/project.pbxproj | 8 ++++++ Submodules/API | 2 +- Submodules/Core | 2 +- 12 files changed, 60 insertions(+), 47 deletions(-) diff --git a/Passepartout-iOS/AppDelegate.swift b/Passepartout-iOS/AppDelegate.swift index 9419c868..02be3bd1 100644 --- a/Passepartout-iOS/AppDelegate.swift +++ b/Passepartout-iOS/AppDelegate.swift @@ -40,11 +40,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele override init() { AppConstants.Log.configure() + InfrastructureFactory.shared.preload() super.init() } func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - InfrastructureFactory.shared.loadCache() Theme.current.applyAppearance() Reviewer.shared.eventCountBeforeRating = AppConstants.Rating.eventCount diff --git a/Passepartout-iOS/Global/IssueReporter.swift b/Passepartout-iOS/Global/IssueReporter.swift index 787e58d8..13ea6dde 100644 --- a/Passepartout-iOS/Global/IssueReporter.swift +++ b/Passepartout-iOS/Global/IssueReporter.swift @@ -77,8 +77,8 @@ class IssueReporter: NSObject { var bodyMetadata = "--\n\n" bodyMetadata += DebugLog(raw: "").decoratedString() if let infrastructure = issue.infrastructure { - bodyMetadata += "Provider: \(infrastructure.name.rawValue)\n" - if let lastUpdated = InfrastructureFactory.shared.modificationDate(for: infrastructure.name) { + bodyMetadata += "Provider: \(infrastructure.name)\n" + if let lastUpdated = InfrastructureFactory.shared.modificationDate(forName: infrastructure.name) { bodyMetadata += "Last updated: \(lastUpdated)\n" } bodyMetadata += "\n" diff --git a/Passepartout-iOS/Global/Product.swift b/Passepartout-iOS/Global/Product.swift index ebd7beff..20e50bab 100644 --- a/Passepartout-iOS/Global/Product.swift +++ b/Passepartout-iOS/Global/Product.swift @@ -111,10 +111,10 @@ enum Product: String { } } -extension Infrastructure.Name { +extension Infrastructure.Metadata { var product: Product { - guard let product = Product(rawValue: "com.algoritmico.ios.Passepartout.providers.\(rawValue)") else { - fatalError("Product not found for provider \(rawValue)") + guard let product = Product(rawValue: "com.algoritmico.ios.Passepartout.providers.\(self)") else { + fatalError("Product not found for provider \(self)") } return product } diff --git a/Passepartout-iOS/Global/ProductManager.swift b/Passepartout-iOS/Global/ProductManager.swift index 11f23aea..245fc6e1 100644 --- a/Passepartout-iOS/Global/ProductManager.swift +++ b/Passepartout-iOS/Global/ProductManager.swift @@ -178,7 +178,7 @@ class ProductManager: NSObject { return true } return purchasedFeatures.contains { - return $0.rawValue.hasSuffix("providers.\(name.rawValue)") + return $0.rawValue.hasSuffix("providers.\(name)") } } diff --git a/Passepartout-iOS/Global/Theme.swift b/Passepartout-iOS/Global/Theme.swift index f60aeaaa..936089cb 100644 --- a/Passepartout-iOS/Global/Theme.swift +++ b/Passepartout-iOS/Global/Theme.swift @@ -189,9 +189,10 @@ extension MFMailComposeViewController { } } -extension Infrastructure.Name { +// FIXME: load from index JSON +extension Infrastructure.Metadata { var logo: UIImage? { - return ImageAsset(name: rawValue.lowercased()).image + return ImageAsset(name: name.lowercased()).image } } diff --git a/Passepartout-iOS/Scenes/AccountViewController.swift b/Passepartout-iOS/Scenes/AccountViewController.swift index e61f4237..df336194 100644 --- a/Passepartout-iOS/Scenes/AccountViewController.swift +++ b/Passepartout-iOS/Scenes/AccountViewController.swift @@ -61,28 +61,32 @@ class AccountViewController: UIViewController, StrongTableHost { guard let name = infrastructureName else { return nil } + // FIXME: make this dynamic? let V = L10n.Core.Account.Sections.Guidance.Footer.Infrastructure.self switch name { case .mullvad: - return V.mullvad(name.rawValue) + return V.mullvad(name) - case .nordVPN: - return V.nordvpn(name.rawValue) + case .nordvpn: + return V.nordvpn(name) case .pia: - return V.pia(name.rawValue) + return V.pia(name) - case .protonVPN: - return V.protonvpn(name.rawValue) + case .protonvpn: + return V.protonvpn(name) - case .tunnelBear: - return V.tunnelbear(name.rawValue) + case .tunnelbear: + return V.tunnelbear(name) - case .vyprVPN: - return V.vyprvpn(name.rawValue) + case .vyprvpn: + return V.vyprvpn(name) case .windscribe: - return V.windscribe(name.rawValue) + return V.windscribe(name) + + default: + return nil } } @@ -266,7 +270,7 @@ extension AccountViewController: UITableViewDataSource, UITableViewDelegate, Fie fatalError("Sign-up shown when not a provider profile") } let cell = Cells.setting.dequeue(from: tableView, for: indexPath) - cell.leftText = L10n.Core.Account.Cells.Signup.caption(name.rawValue) + cell.leftText = L10n.Core.Account.Cells.Signup.caption(name) cell.applyAction(.current) return cell } diff --git a/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift b/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift index b9891e06..0a69379f 100644 --- a/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift +++ b/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift @@ -39,7 +39,7 @@ class OrganizerViewController: UITableViewController, StrongTableHost { private var hosts: [String] = [] - private var availableProviderNames: [Infrastructure.Name]? + private var availableProviders: [Infrastructure.Metadata]? private var didShowSubreddit = false @@ -177,7 +177,7 @@ class OrganizerViewController: UITableViewController, StrongTableHost { vc.setProfile(selectedProfile) } else if let providerVC = destination as? WizardProviderViewController { - providerVC.availableNames = availableProviderNames ?? [] + providerVC.available = availableProviders ?? [] } } @@ -195,8 +195,8 @@ class OrganizerViewController: UITableViewController, StrongTableHost { } private func addNewProvider() { - let names = service.availableProviderNames() - guard !names.isEmpty else { + let providers = service.availableProviders() + guard !providers.isEmpty else { let alert = UIAlertController.asAlert( L10n.Core.Organizer.Sections.Providers.header, L10n.Core.Organizer.Alerts.ExhaustedProviders.message @@ -205,7 +205,7 @@ class OrganizerViewController: UITableViewController, StrongTableHost { present(alert, animated: true, completion: nil) return } - availableProviderNames = names + availableProviders = providers perform(segue: StoryboardSegue.Organizer.addProviderSegueIdentifier) } @@ -479,12 +479,13 @@ extension OrganizerViewController { case .profile: let cell = Cells.setting.dequeue(from: tableView, for: indexPath) let rowProfile = profileKey(at: indexPath) - if rowProfile.context == .provider, let providerName = Infrastructure.Name(rawValue: rowProfile.id) { - cell.imageView?.image = providerName.logo + if rowProfile.context == .provider, let metadata = InfrastructureFactory.shared.metadata(forName: rowProfile.id) { + cell.imageView?.image = metadata.logo + cell.leftText = metadata.description } else { cell.imageView?.image = nil + cell.leftText = rowProfile.id } - cell.leftText = rowProfile.id cell.rightText = service.isActiveProfile(rowProfile) ? L10n.Core.Organizer.Cells.Profile.Value.current : nil return cell diff --git a/Passepartout-iOS/Scenes/Organizer/WizardProviderViewController.swift b/Passepartout-iOS/Scenes/Organizer/WizardProviderViewController.swift index 18e12271..277fb019 100644 --- a/Passepartout-iOS/Scenes/Organizer/WizardProviderViewController.swift +++ b/Passepartout-iOS/Scenes/Organizer/WizardProviderViewController.swift @@ -27,7 +27,7 @@ import UIKit import PassepartoutCore class WizardProviderViewController: UITableViewController { - var availableNames: [Infrastructure.Name] = [] + var available: [Infrastructure.Metadata] = [] private var createdProfile: ProviderConnectionProfile? @@ -37,17 +37,17 @@ class WizardProviderViewController: UITableViewController { title = L10n.Core.Organizer.Sections.Providers.header } - private func next(withName name: Infrastructure.Name) { - guard ProductManager.shared.isEligible(forProvider: name) else { - presentPurchaseScreen(forProduct: name.product) + private func next(withMetadata metadata: Infrastructure.Metadata) { + guard ProductManager.shared.isEligible(forProvider: metadata.name) else { + presentPurchaseScreen(forProduct: metadata.product) return } - let profile = ProviderConnectionProfile(name: name) + let profile = ProviderConnectionProfile(name: metadata.name) createdProfile = profile let accountVC = StoryboardScene.Main.accountIdentifier.instantiate() - let infrastructure = InfrastructureFactory.shared.get(name) + let infrastructure = InfrastructureFactory.shared.infrastructure(forName: metadata.name) accountVC.usernamePlaceholder = infrastructure.defaults.username accountVC.infrastructureName = infrastructure.name accountVC.delegate = self @@ -73,20 +73,20 @@ class WizardProviderViewController: UITableViewController { extension WizardProviderViewController { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return availableNames.count + return available.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let name = availableNames[indexPath.row] + let metadata = available[indexPath.row] let cell = Cells.setting.dequeue(from: tableView, for: indexPath) - cell.imageView?.image = name.logo - cell.leftText = name.rawValue + cell.imageView?.image = metadata.logo + cell.leftText = metadata.description return cell } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let name = availableNames[indexPath.row] - next(withName: name) + let metadata = available[indexPath.row] + next(withMetadata: metadata) } } diff --git a/Passepartout-iOS/Scenes/ServiceViewController.swift b/Passepartout-iOS/Scenes/ServiceViewController.swift index f34c0cca..0a2e5f15 100644 --- a/Passepartout-iOS/Scenes/ServiceViewController.swift +++ b/Passepartout-iOS/Scenes/ServiceViewController.swift @@ -79,7 +79,7 @@ class ServiceViewController: UIViewController, StrongTableHost { self.profile = profile vpn.profile = profile - title = profile?.id + title = profile?.screenTitle navigationItem.rightBarButtonItem = (profile?.context == .host) ? itemEdit : nil if reloadingViews { reloadModel() @@ -95,10 +95,9 @@ class ServiceViewController: UIViewController, StrongTableHost { setProfile(service.activeProfile) } if let providerProfile = profile as? ProviderConnectionProfile { - lastInfrastructureUpdate = InfrastructureFactory.shared.modificationDate(for: providerProfile.name) + lastInfrastructureUpdate = InfrastructureFactory.shared.modificationDate(forName: providerProfile.name) } - title = profile?.id navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem navigationItem.leftItemsSupplementBackButton = true @@ -596,7 +595,7 @@ class ServiceViewController: UIViewController, StrongTableHost { let alert = UIAlertController.asAlert( L10n.Core.Service.Alerts.Download.title, - L10n.Core.Service.Alerts.Download.message(providerProfile.name.rawValue) + L10n.Core.Service.Alerts.Download.message(providerProfile.name) ) alert.addCancelAction(L10n.Core.Global.cancel) alert.addPreferredAction(L10n.Core.Global.ok) { diff --git a/Passepartout.xcodeproj/project.pbxproj b/Passepartout.xcodeproj/project.pbxproj index 33910cfd..de41e9eb 100644 --- a/Passepartout.xcodeproj/project.pbxproj +++ b/Passepartout.xcodeproj/project.pbxproj @@ -89,6 +89,8 @@ 0E9CD7872257462800D033B4 /* Providers.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E9CD7862257462800D033B4 /* Providers.xcassets */; }; 0E9CD789225746B300D033B4 /* Flags.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E9CD788225746B300D033B4 /* Flags.xcassets */; }; 0E9CDB6723604AD5006733B4 /* ServerNetworkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9CDB6623604AD5006733B4 /* ServerNetworkViewController.swift */; }; + 0EA84515238A9B5200EFC500 /* Infrastructure+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA84514238A9B5100EFC500 /* Infrastructure+Name.swift */; }; + 0EA8451A238C2AB500EFC500 /* Infrastructure+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA84519238C2AB500EFC500 /* Infrastructure+Metadata.swift */; }; 0EAAD71920E6669A0088754A /* GroupConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE8DED20C93E4C004C739C /* GroupConstants.swift */; }; 0EB60FDA2111136E00AD27F3 /* UITextView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB60FD92111136E00AD27F3 /* UITextView+Search.swift */; }; 0EB67D6B2184581E00BA6200 /* ImportedHostsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */; }; @@ -258,6 +260,8 @@ 0E9CD7862257462800D033B4 /* Providers.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Providers.xcassets; sourceTree = ""; }; 0E9CD788225746B300D033B4 /* Flags.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Flags.xcassets; sourceTree = ""; }; 0E9CDB6623604AD5006733B4 /* ServerNetworkViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerNetworkViewController.swift; sourceTree = ""; }; + 0EA84514238A9B5100EFC500 /* Infrastructure+Name.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Infrastructure+Name.swift"; path = "Submodules/Core/Passepartout/Sources/Services/Infrastructure+Name.swift"; sourceTree = SOURCE_ROOT; }; + 0EA84519238C2AB500EFC500 /* Infrastructure+Metadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Infrastructure+Metadata.swift"; path = "Submodules/Core/Passepartout/Sources/Services/Infrastructure+Metadata.swift"; sourceTree = SOURCE_ROOT; }; 0EB60FD92111136E00AD27F3 /* UITextView+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Search.swift"; sourceTree = ""; }; 0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportedHostsViewController.swift; sourceTree = ""; }; 0EB9EB7223867E7F009C0A1C /* TrustedNetworksUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrustedNetworksUI.swift; sourceTree = ""; }; @@ -545,6 +549,8 @@ isa = PBXGroup; children = ( 0ED31C1120CF0ABA0027975F /* Infrastructure.swift */, + 0EA84519238C2AB500EFC500 /* Infrastructure+Metadata.swift */, + 0EA84514238A9B5100EFC500 /* Infrastructure+Name.swift */, 0EBE3A83213C6ADE00BFA2F5 /* InfrastructureFactory.swift */, 0E8D97E121388B52006FB4A0 /* InfrastructurePreset.swift */, 0ED31C0F20CF09A30027975F /* Pool.swift */, @@ -985,6 +991,7 @@ 0E3152CA223FA04D00F61841 /* InfrastructurePreset.swift in Sources */, 0E3152CE223FA05400F61841 /* ConnectionService.swift in Sources */, 0ED993B1223FF8C700B0F9C9 /* IntentDispatcher.swift in Sources */, + 0EA84515238A9B5200EFC500 /* Infrastructure+Name.swift in Sources */, 0EEF23412321AC55000AEBE3 /* Issue.swift in Sources */, 0E3152C3223FA04800F61841 /* StandardVPNProvider.swift in Sources */, 0E3152D1223FA05400F61841 /* Credentials.swift in Sources */, @@ -996,6 +1003,7 @@ 0EFB901822764689006405E4 /* ProfileNetworkSettings.swift in Sources */, 0E3152C0223FA03D00F61841 /* Utils.swift in Sources */, 0E3152CB223FA04D00F61841 /* Pool.swift in Sources */, + 0EA8451A238C2AB500EFC500 /* Infrastructure+Metadata.swift in Sources */, 0EB9EB7323867E7F009C0A1C /* TrustedNetworksUI.swift in Sources */, 0E3CAFC0229AAE770008E5C8 /* Intents.intentdefinition in Sources */, 0E3152C7223FA04800F61841 /* VPNStatus.swift in Sources */, diff --git a/Submodules/API b/Submodules/API index 60b5c25a..0d2991f7 160000 --- a/Submodules/API +++ b/Submodules/API @@ -1 +1 @@ -Subproject commit 60b5c25a3cc79e0bffdd551c4593bdd18d291719 +Subproject commit 0d2991f706fbb2a190ecccd390d5c993efd67c92 diff --git a/Submodules/Core b/Submodules/Core index 79cc4a73..c3ed76f0 160000 --- a/Submodules/Core +++ b/Submodules/Core @@ -1 +1 @@ -Subproject commit 79cc4a739978b6fc98e910c65d7913431cb41915 +Subproject commit c3ed76f010c51139eda1967055998a2308b1c730 From 0c9242c5d41d4a3e3e4189e2d4d45c25680ca9d2 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Thu, 28 Nov 2019 10:58:19 +0100 Subject: [PATCH 2/7] Use provider description in guidance footer Was showing full lowercase. --- .../Scenes/AccountViewController.swift | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Passepartout-iOS/Scenes/AccountViewController.swift b/Passepartout-iOS/Scenes/AccountViewController.swift index df336194..ec2923ba 100644 --- a/Passepartout-iOS/Scenes/AccountViewController.swift +++ b/Passepartout-iOS/Scenes/AccountViewController.swift @@ -58,32 +58,32 @@ class AccountViewController: UIViewController, StrongTableHost { } private var guidanceString: String? { - guard let name = infrastructureName else { + guard let name = infrastructureName, let metadata = InfrastructureFactory.shared.metadata(forName: name) else { return nil } - // FIXME: make this dynamic? + // XXX: should make this dynamic let V = L10n.Core.Account.Sections.Guidance.Footer.Infrastructure.self - switch name { + switch metadata.name { case .mullvad: - return V.mullvad(name) + return V.mullvad(metadata.description) case .nordvpn: - return V.nordvpn(name) + return V.nordvpn(metadata.description) case .pia: - return V.pia(name) + return V.pia(metadata.description) case .protonvpn: - return V.protonvpn(name) + return V.protonvpn(metadata.description) case .tunnelbear: - return V.tunnelbear(name) + return V.tunnelbear(metadata.description) case .vyprvpn: - return V.vyprvpn(name) + return V.vyprvpn(metadata.description) case .windscribe: - return V.windscribe(name) + return V.windscribe(metadata.description) default: return nil From d4977f499ae7bb0d21fd93126ee6ed77c7ae1f8f Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Thu, 28 Nov 2019 10:09:28 +0100 Subject: [PATCH 3/7] Add "Update list" stub in provider wizard --- .../Global/SwiftGen+Strings.swift | 8 +++ .../WizardProviderViewController.swift | 68 +++++++++++++++++-- Submodules/Core | 2 +- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/Passepartout-iOS/Global/SwiftGen+Strings.swift b/Passepartout-iOS/Global/SwiftGen+Strings.swift index a351a67b..82549a11 100644 --- a/Passepartout-iOS/Global/SwiftGen+Strings.swift +++ b/Passepartout-iOS/Global/SwiftGen+Strings.swift @@ -1047,6 +1047,14 @@ internal enum L10n { } } } + internal enum Provider { + internal enum Cells { + internal enum UpdateList { + /// Update list + internal static let caption = L10n.tr("Core", "wizards.provider.cells.update_list.caption") + } + } + } } } } diff --git a/Passepartout-iOS/Scenes/Organizer/WizardProviderViewController.swift b/Passepartout-iOS/Scenes/Organizer/WizardProviderViewController.swift index 277fb019..90a387d2 100644 --- a/Passepartout-iOS/Scenes/Organizer/WizardProviderViewController.swift +++ b/Passepartout-iOS/Scenes/Organizer/WizardProviderViewController.swift @@ -25,16 +25,32 @@ import UIKit import PassepartoutCore +import Convenience -class WizardProviderViewController: UITableViewController { +class WizardProviderViewController: UITableViewController, StrongTableHost { var available: [Infrastructure.Metadata] = [] private var createdProfile: ProviderConnectionProfile? + + // MARK: StrongTableHost + + let model = StrongTableModel() + + func reloadModel() { + model.clear() + model.add(.availableProviders) + model.add(.listActions) + model.set(.provider, count: available.count, forSection: .availableProviders) + model.set([.updateList], forSection: .listActions) + } + + // MARK: UIViewController override func viewDidLoad() { super.viewDidLoad() title = L10n.Core.Organizer.Sections.Providers.header + reloadModel() } private func next(withMetadata metadata: Infrastructure.Metadata) { @@ -63,6 +79,10 @@ class WizardProviderViewController: UITableViewController { service.addOrReplaceProfile(profile, credentials: credentials) } } + + private func updateProvidersList() { + // FIXME: update providers + } @IBAction private func close() { dismiss(animated: true, completion: nil) @@ -72,21 +92,55 @@ class WizardProviderViewController: UITableViewController { // MARK: - extension WizardProviderViewController { + enum SectionType { + case availableProviders + + case listActions + } + + enum RowType { + case provider + + case updateList + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return model.numberOfSections + } + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return available.count + return model.numberOfRows(forSection: section) } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let metadata = available[indexPath.row] + let row = model.row(at: indexPath) let cell = Cells.setting.dequeue(from: tableView, for: indexPath) - cell.imageView?.image = metadata.logo - cell.leftText = metadata.description + switch row { + case .provider: + let metadata = available[indexPath.row] + cell.apply(.current) + cell.imageView?.image = metadata.logo + cell.leftText = metadata.description + + case .updateList: + cell.applyAction(.current) + cell.imageView?.image = nil + cell.leftText = L10n.Core.Wizards.Provider.Cells.UpdateList.caption + } return cell } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let metadata = available[indexPath.row] - next(withMetadata: metadata) + let row = model.row(at: indexPath) + switch row { + case .provider: + let metadata = available[indexPath.row] + next(withMetadata: metadata) + + case .updateList: + tableView.deselectRow(at: indexPath, animated: true) + updateProvidersList() + } } } diff --git a/Submodules/Core b/Submodules/Core index c3ed76f0..27aae752 160000 --- a/Submodules/Core +++ b/Submodules/Core @@ -1 +1 @@ -Subproject commit c3ed76f010c51139eda1967055998a2308b1c730 +Subproject commit 27aae7520abbe5c58cbeda82a51ea28ff804f766 From e5325cea7e5a0f04e306cd5c8aa352f3173ace00 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Thu, 28 Nov 2019 11:22:10 +0100 Subject: [PATCH 4/7] Make in-app product identifiers dynamic --- Passepartout-iOS/Global/Product.swift | 104 +++++++++++-------- Passepartout-iOS/Global/ProductManager.swift | 2 + Submodules/API | 2 +- Submodules/Core | 2 +- 4 files changed, 66 insertions(+), 44 deletions(-) diff --git a/Passepartout-iOS/Global/Product.swift b/Passepartout-iOS/Global/Product.swift index 20e50bab..5b52479a 100644 --- a/Passepartout-iOS/Global/Product.swift +++ b/Passepartout-iOS/Global/Product.swift @@ -26,21 +26,28 @@ import Foundation import PassepartoutCore -enum Product: String { +struct Product: RawRepresentable, Equatable, Hashable { + private static let bundle = "com.algoritmico.ios.Passepartout" + + private static let donationsBundle = "\(bundle).donations" + + private static let featuresBundle = "\(bundle).features" + + private static let providersBundle = "\(bundle).providers" // MARK: Donations - case tinyDonation = "com.algoritmico.ios.Passepartout.donations.Tiny" + static let tinyDonation = Product(donationDescription: "Tiny") - case smallDonation = "com.algoritmico.ios.Passepartout.donations.Small" + static let smallDonation = Product(donationDescription: "Small") - case mediumDonation = "com.algoritmico.ios.Passepartout.donations.Medium" + static let mediumDonation = Product(donationDescription: "Medium") - case bigDonation = "com.algoritmico.ios.Passepartout.donations.Big" + static let bigDonation = Product(donationDescription: "Big") - case hugeDonation = "com.algoritmico.ios.Passepartout.donations.Huge" + static let hugeDonation = Product(donationDescription: "Huge") - case maxiDonation = "com.algoritmico.ios.Passepartout.donations.Maxi" + static let maxiDonation = Product(donationDescription: "Maxi") static let allDonations: [Product] = [ .tinyDonation, @@ -51,15 +58,19 @@ enum Product: String { .maxiDonation ] + private init(donationDescription: String) { + self.init(rawValue: "\(Product.donationsBundle).\(donationDescription)")! + } + // MARK: Features - case unlimitedHosts = "com.algoritmico.ios.Passepartout.features.unlimited_hosts" + static let unlimitedHosts = Product(featureId: "unlimited_hosts") - case trustedNetworks = "com.algoritmico.ios.Passepartout.features.trusted_networks" + static let trustedNetworks = Product(featureId: "trusted_networks") - case siriShortcuts = "com.algoritmico.ios.Passepartout.features.siri" + static let siriShortcuts = Product(featureId: "features.siri") - case fullVersion = "com.algoritmico.ios.Passepartout.features.full_version" + static let fullVersion = Product(featureId: "full_version") static let allFeatures: [Product] = [ .unlimitedHosts, @@ -67,55 +78,64 @@ enum Product: String { .siriShortcuts, .fullVersion ] + + private init(featureId: String) { + self.init(rawValue: "\(Product.featuresBundle).\(featureId)")! + } // MARK: Providers - case mullvad = "com.algoritmico.ios.Passepartout.providers.Mullvad" - - case nordVPN = "com.algoritmico.ios.Passepartout.providers.NordVPN" - - case pia = "com.algoritmico.ios.Passepartout.providers.PIA" - - case protonVPN = "com.algoritmico.ios.Passepartout.providers.ProtonVPN" - - case tunnelBear = "com.algoritmico.ios.Passepartout.providers.TunnelBear" - - case vyprVPN = "com.algoritmico.ios.Passepartout.providers.VyprVPN" - - case windscribe = "com.algoritmico.ios.Passepartout.providers.Windscribe" - - static let allProviders: [Product] = [ - .mullvad, - .nordVPN, - .pia, - .protonVPN, - .tunnelBear, - .vyprVPN, - .windscribe - ] + static var allProviders: [Product] { + return InfrastructureFactory.shared.allMetadata.map { + return Product(providerId: $0.description) + } + } + fileprivate init(providerId: String) { + self.init(rawValue: "\(Product.providersBundle).\(providerId)")! + } + // MARK: All - static let all: [Product] = allDonations + allFeatures + allProviders + static var all: [Product] { + return allDonations + allFeatures + allProviders + } var isDonation: Bool { - return Product.allDonations.contains(self) + return rawValue.hasPrefix(Product.donationsBundle) } var isFeature: Bool { - return Product.allFeatures.contains(self) + return rawValue.hasPrefix(Product.featuresBundle) } var isProvider: Bool { - return Product.allProviders.contains(self) + return rawValue.hasPrefix(Product.providersBundle) + } + + // MARK: RawRepresentable + + let rawValue: String + + init?(rawValue: String) { + self.rawValue = rawValue + } + + // MARK: Equatable + + static func ==(lhs: Product, rhs: Product) -> Bool { + return lhs.rawValue == rhs.rawValue + } + + // MARK: Hashable + + func hash(into hasher: inout Hasher) { + rawValue.hash(into: &hasher) } } extension Infrastructure.Metadata { var product: Product { - guard let product = Product(rawValue: "com.algoritmico.ios.Passepartout.providers.\(self)") else { - fatalError("Product not found for provider \(self)") - } - return product + return Product(providerId: description) } } diff --git a/Passepartout-iOS/Global/ProductManager.swift b/Passepartout-iOS/Global/ProductManager.swift index 245fc6e1..7758e70c 100644 --- a/Passepartout-iOS/Global/ProductManager.swift +++ b/Passepartout-iOS/Global/ProductManager.swift @@ -72,6 +72,8 @@ class ProductManager: NSObject { return } inApp.requestProducts(withIdentifiers: Product.all) { _ in + log.debug("In-app products: \(self.inApp.products.map { $0.productIdentifier })") + completionHandler?(self.inApp.products) } } diff --git a/Submodules/API b/Submodules/API index 0d2991f7..72d464ab 160000 --- a/Submodules/API +++ b/Submodules/API @@ -1 +1 @@ -Subproject commit 0d2991f706fbb2a190ecccd390d5c993efd67c92 +Subproject commit 72d464ab9291d7cb7521603742b04b3928d1d2c5 diff --git a/Submodules/Core b/Submodules/Core index 27aae752..010fdccd 160000 --- a/Submodules/Core +++ b/Submodules/Core @@ -1 +1 @@ -Subproject commit 27aae7520abbe5c58cbeda82a51ea28ff804f766 +Subproject commit 010fdccdf019f73f399f99fde2bc6bb1571b596b From 28a7dcdcf7381f888a595806d00644400f8e93b5 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Thu, 28 Nov 2019 10:27:47 +0100 Subject: [PATCH 5/7] Update providers via web service Reload products after update. Always re-list products! Had to fix web services in Convenience, completion handler not invoked from main queue. --- Passepartout-iOS/Global/ProductManager.swift | 4 --- .../Organizer/OrganizerViewController.swift | 8 +----- .../WizardProviderViewController.swift | 22 ++++++++++++++-- Passepartout.xcodeproj/project.pbxproj | 8 +++--- Podfile | 2 +- Podfile.lock | 26 +++++++++---------- 6 files changed, 39 insertions(+), 31 deletions(-) diff --git a/Passepartout-iOS/Global/ProductManager.swift b/Passepartout-iOS/Global/ProductManager.swift index 7758e70c..32039f2c 100644 --- a/Passepartout-iOS/Global/ProductManager.swift +++ b/Passepartout-iOS/Global/ProductManager.swift @@ -67,10 +67,6 @@ class ProductManager: NSObject { } func listProducts(completionHandler: (([SKProduct]) -> Void)?) { - guard inApp.products.isEmpty else { - completionHandler?(inApp.products) - return - } inApp.requestProducts(withIdentifiers: Product.all) { _ in log.debug("In-app products: \(self.inApp.products.map { $0.productIdentifier })") diff --git a/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift b/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift index 0a69379f..f87c65a4 100644 --- a/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift +++ b/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift @@ -39,8 +39,6 @@ class OrganizerViewController: UITableViewController, StrongTableHost { private var hosts: [String] = [] - private var availableProviders: [Infrastructure.Metadata]? - private var didShowSubreddit = false // MARK: StrongTableHost @@ -176,8 +174,6 @@ class OrganizerViewController: UITableViewController, StrongTableHost { } vc.setProfile(selectedProfile) - } else if let providerVC = destination as? WizardProviderViewController { - providerVC.available = availableProviders ?? [] } } @@ -195,8 +191,7 @@ class OrganizerViewController: UITableViewController, StrongTableHost { } private func addNewProvider() { - let providers = service.availableProviders() - guard !providers.isEmpty else { + guard service.hasAvailableProviders() else { let alert = UIAlertController.asAlert( L10n.Core.Organizer.Sections.Providers.header, L10n.Core.Organizer.Alerts.ExhaustedProviders.message @@ -205,7 +200,6 @@ class OrganizerViewController: UITableViewController, StrongTableHost { present(alert, animated: true, completion: nil) return } - availableProviders = providers perform(segue: StoryboardSegue.Organizer.addProviderSegueIdentifier) } diff --git a/Passepartout-iOS/Scenes/Organizer/WizardProviderViewController.swift b/Passepartout-iOS/Scenes/Organizer/WizardProviderViewController.swift index 90a387d2..886d9451 100644 --- a/Passepartout-iOS/Scenes/Organizer/WizardProviderViewController.swift +++ b/Passepartout-iOS/Scenes/Organizer/WizardProviderViewController.swift @@ -26,9 +26,12 @@ import UIKit import PassepartoutCore import Convenience +import SwiftyBeaver + +private let log = SwiftyBeaver.self class WizardProviderViewController: UITableViewController, StrongTableHost { - var available: [Infrastructure.Metadata] = [] + private var available: [Infrastructure.Metadata] = [] private var createdProfile: ProviderConnectionProfile? @@ -37,6 +40,8 @@ class WizardProviderViewController: UITableViewController, StrongTableHost { let model = StrongTableModel() func reloadModel() { + available = TransientStore.shared.service.availableProviders() + model.clear() model.add(.availableProviders) model.add(.listActions) @@ -81,7 +86,20 @@ class WizardProviderViewController: UITableViewController, StrongTableHost { } private func updateProvidersList() { - // FIXME: update providers + let hud = HUD(view: view) + InfrastructureFactory.shared.updateIndex { [weak self] in + if let error = $0 { + hud.hide() + log.error("Unable to update providers list: \(error)") + return + } + + ProductManager.shared.listProducts { _ in + self?.reloadModel() + self?.tableView.reloadData() + hud.hide() + } + } } @IBAction private func close() { diff --git a/Passepartout.xcodeproj/project.pbxproj b/Passepartout.xcodeproj/project.pbxproj index de41e9eb..2470b957 100644 --- a/Passepartout.xcodeproj/project.pbxproj +++ b/Passepartout.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 0E0C0729236087A100155AAC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E0C072B236087A100155AAC /* InfoPlist.strings */; }; 0E1066C920E0F84A004F98B7 /* Cells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1066C820E0F84A004F98B7 /* Cells.swift */; }; 0E158ADA20E11B0B00C85A82 /* EndpointViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E158AD920E11B0B00C85A82 /* EndpointViewController.swift */; }; + 0E1C0A4F238FC7A5009FC087 /* InfrastructureFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1C0A4E238FC7A5009FC087 /* InfrastructureFactory.swift */; }; 0E1D72B2213BFFCF00BA1586 /* ProviderPresetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1D72B1213BFFCF00BA1586 /* ProviderPresetViewController.swift */; }; 0E1D72B4213C118500BA1586 /* ConfigurationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1D72B3213C118500BA1586 /* ConfigurationViewController.swift */; }; 0E24273A225950450064A1A3 /* About.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E24273C225950450064A1A3 /* About.storyboard */; }; @@ -38,7 +39,6 @@ 0E3152C6223FA04800F61841 /* VPNProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AF1214177920004D387 /* VPNProvider.swift */; }; 0E3152C7223FA04800F61841 /* VPNStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5E5DDE215119AF00E318A3 /* VPNStatus.swift */; }; 0E3152C8223FA04D00F61841 /* Infrastructure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED31C1120CF0ABA0027975F /* Infrastructure.swift */; }; - 0E3152C9223FA04D00F61841 /* InfrastructureFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE3A83213C6ADE00BFA2F5 /* InfrastructureFactory.swift */; }; 0E3152CA223FA04D00F61841 /* InfrastructurePreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8D97E121388B52006FB4A0 /* InfrastructurePreset.swift */; }; 0E3152CB223FA04D00F61841 /* Pool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED31C0F20CF09A30027975F /* Pool.swift */; }; 0E3152CC223FA04D00F61841 /* WebServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E39BCEF214B9EF10035E9DE /* WebServices.swift */; }; @@ -171,6 +171,7 @@ 0E0C072C236087C800155AAC /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; 0E1066C820E0F84A004F98B7 /* Cells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cells.swift; sourceTree = ""; }; 0E158AD920E11B0B00C85A82 /* EndpointViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EndpointViewController.swift; sourceTree = ""; }; + 0E1C0A4E238FC7A5009FC087 /* InfrastructureFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InfrastructureFactory.swift; path = Submodules/Core/Passepartout/Sources/Services/InfrastructureFactory.swift; sourceTree = SOURCE_ROOT; }; 0E1D72B1213BFFCF00BA1586 /* ProviderPresetViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProviderPresetViewController.swift; sourceTree = ""; }; 0E1D72B3213C118500BA1586 /* ConfigurationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationViewController.swift; sourceTree = ""; }; 0E23B4A12298559800304C30 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; @@ -276,7 +277,6 @@ 0EBE2FD72360F89600F0D5AB /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; 0EBE2FD82360F89600F0D5AB /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = ""; }; 0EBE3A78213C4E5400BFA2F5 /* OrganizerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrganizerViewController.swift; sourceTree = ""; }; - 0EBE3A83213C6ADE00BFA2F5 /* InfrastructureFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfrastructureFactory.swift; sourceTree = ""; }; 0EBE3A8F213C6F4000BFA2F5 /* TrustPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrustPolicy.swift; sourceTree = ""; }; 0EBE3A9E213DC1A100BFA2F5 /* ConnectionProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionProfile.swift; sourceTree = ""; }; 0EBE3A9F213DC1A100BFA2F5 /* ConnectionService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionService.swift; sourceTree = ""; }; @@ -551,7 +551,7 @@ 0ED31C1120CF0ABA0027975F /* Infrastructure.swift */, 0EA84519238C2AB500EFC500 /* Infrastructure+Metadata.swift */, 0EA84514238A9B5100EFC500 /* Infrastructure+Name.swift */, - 0EBE3A83213C6ADE00BFA2F5 /* InfrastructureFactory.swift */, + 0E1C0A4E238FC7A5009FC087 /* InfrastructureFactory.swift */, 0E8D97E121388B52006FB4A0 /* InfrastructurePreset.swift */, 0ED31C0F20CF09A30027975F /* Pool.swift */, 0E66A26F225FE25800F9C779 /* PoolCategory.swift */, @@ -975,6 +975,7 @@ 0E3152C6223FA04800F61841 /* VPNProvider.swift in Sources */, 0E3152DA223FA05800F61841 /* PlaceholderConnectionProfile.swift in Sources */, 0E3152D0223FA05400F61841 /* ConnectionService+Migration.swift in Sources */, + 0E1C0A4F238FC7A5009FC087 /* InfrastructureFactory.swift in Sources */, 0E3152D5223FA05400F61841 /* SessionProxy+Communication.swift in Sources */, 0E3152D7223FA05400F61841 /* TrustedNetworks.swift in Sources */, 0E3152DB223FA05800F61841 /* ProfileKey.swift in Sources */, @@ -997,7 +998,6 @@ 0E3152D1223FA05400F61841 /* Credentials.swift in Sources */, 0E3152CD223FA05400F61841 /* ConnectionProfile.swift in Sources */, 0E3152BC223FA03D00F61841 /* ApplicationError.swift in Sources */, - 0E3152C9223FA04D00F61841 /* InfrastructureFactory.swift in Sources */, 0E3152D3223FA05400F61841 /* EndpointDataSource.swift in Sources */, 0E3152D4223FA05400F61841 /* Preferences.swift in Sources */, 0EFB901822764689006405E4 /* ProfileNetworkSettings.swift in Sources */, diff --git a/Podfile b/Podfile index 874498b9..dc6dfaf8 100644 --- a/Podfile +++ b/Podfile @@ -14,7 +14,7 @@ def shared_pods pod 'SSZipArchive' for spec in ['About', 'Alerts', 'Dialogs', 'InApp', 'Misc', 'Options', 'Persistence', 'Reviewer', 'Tables', 'WebServices'] do - pod "Convenience/#{spec}", :git => 'https://github.com/keeshux/convenience', :commit => 'cfd2e57' + pod "Convenience/#{spec}", :git => 'https://github.com/keeshux/convenience', :commit => '7fe7dcf' #pod "Convenience/#{spec}", :path => '../../personal/convenience' end end diff --git a/Podfile.lock b/Podfile.lock index 6f56948e..a7778eea 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -36,16 +36,16 @@ PODS: - TunnelKit/Core DEPENDENCIES: - - Convenience/About (from `https://github.com/keeshux/convenience`, commit `cfd2e57`) - - Convenience/Alerts (from `https://github.com/keeshux/convenience`, commit `cfd2e57`) - - Convenience/Dialogs (from `https://github.com/keeshux/convenience`, commit `cfd2e57`) - - Convenience/InApp (from `https://github.com/keeshux/convenience`, commit `cfd2e57`) - - Convenience/Misc (from `https://github.com/keeshux/convenience`, commit `cfd2e57`) - - Convenience/Options (from `https://github.com/keeshux/convenience`, commit `cfd2e57`) - - Convenience/Persistence (from `https://github.com/keeshux/convenience`, commit `cfd2e57`) - - Convenience/Reviewer (from `https://github.com/keeshux/convenience`, commit `cfd2e57`) - - Convenience/Tables (from `https://github.com/keeshux/convenience`, commit `cfd2e57`) - - Convenience/WebServices (from `https://github.com/keeshux/convenience`, commit `cfd2e57`) + - Convenience/About (from `https://github.com/keeshux/convenience`, commit `7fe7dcf`) + - Convenience/Alerts (from `https://github.com/keeshux/convenience`, commit `7fe7dcf`) + - Convenience/Dialogs (from `https://github.com/keeshux/convenience`, commit `7fe7dcf`) + - Convenience/InApp (from `https://github.com/keeshux/convenience`, commit `7fe7dcf`) + - Convenience/Misc (from `https://github.com/keeshux/convenience`, commit `7fe7dcf`) + - Convenience/Options (from `https://github.com/keeshux/convenience`, commit `7fe7dcf`) + - Convenience/Persistence (from `https://github.com/keeshux/convenience`, commit `7fe7dcf`) + - Convenience/Reviewer (from `https://github.com/keeshux/convenience`, commit `7fe7dcf`) + - Convenience/Tables (from `https://github.com/keeshux/convenience`, commit `7fe7dcf`) + - Convenience/WebServices (from `https://github.com/keeshux/convenience`, commit `7fe7dcf`) - Kvitto - MBProgressHUD - SSZipArchive @@ -63,7 +63,7 @@ SPEC REPOS: EXTERNAL SOURCES: Convenience: - :commit: cfd2e57 + :commit: 7fe7dcf :git: https://github.com/keeshux/convenience TunnelKit: :commit: 8b17a13 @@ -71,7 +71,7 @@ EXTERNAL SOURCES: CHECKOUT OPTIONS: Convenience: - :commit: cfd2e57 + :commit: 7fe7dcf :git: https://github.com/keeshux/convenience TunnelKit: :commit: 8b17a13 @@ -87,6 +87,6 @@ SPEC CHECKSUMS: SwiftyBeaver: 3d3e93a12d648bd400b6f2948a7ef128b5b183c7 TunnelKit: 4b70c0d8b6727b407248b4271b7613225f63204b -PODFILE CHECKSUM: 51463b0e66ae367dd35c273e67cfc84659a41432 +PODFILE CHECKSUM: 10c7cb879b6ee3d5185f541e6a2c6d14e988ff0b COCOAPODS: 1.8.4 From 1619c7f46ce5b10cb3c22631c54da3ab914e5065 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Thu, 28 Nov 2019 11:42:23 +0100 Subject: [PATCH 6/7] Use placeholder when provider logo is missing So far it's what happens for dynamically added providers, because there is no local asset. --- Passepartout-iOS/Global/SwiftGen+Assets.swift | 1 + Passepartout-iOS/Global/Theme.swift | 2 +- .../placeholder.imageset/Contents.json | 22 ++++++++++++++++++ .../placeholder.imageset/placeholder@2x.png | Bin 0 -> 2362 bytes .../placeholder.imageset/placeholder@3x.png | Bin 0 -> 3468 bytes .../Organizer/OrganizerViewController.swift | 11 ++++++--- 6 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 Passepartout-iOS/Providers.xcassets/placeholder.imageset/Contents.json create mode 100644 Passepartout-iOS/Providers.xcassets/placeholder.imageset/placeholder@2x.png create mode 100644 Passepartout-iOS/Providers.xcassets/placeholder.imageset/placeholder@3x.png diff --git a/Passepartout-iOS/Global/SwiftGen+Assets.swift b/Passepartout-iOS/Global/SwiftGen+Assets.swift index dc5ead11..5f14a848 100644 --- a/Passepartout-iOS/Global/SwiftGen+Assets.swift +++ b/Passepartout-iOS/Global/SwiftGen+Assets.swift @@ -284,6 +284,7 @@ internal enum Asset { internal static let mullvad = ImageAsset(name: "mullvad") internal static let nordvpn = ImageAsset(name: "nordvpn") internal static let pia = ImageAsset(name: "pia") + internal static let placeholder = ImageAsset(name: "placeholder") internal static let protonvpn = ImageAsset(name: "protonvpn") internal static let tunnelbear = ImageAsset(name: "tunnelbear") internal static let vyprvpn = ImageAsset(name: "vyprvpn") diff --git a/Passepartout-iOS/Global/Theme.swift b/Passepartout-iOS/Global/Theme.swift index 936089cb..bd894edc 100644 --- a/Passepartout-iOS/Global/Theme.swift +++ b/Passepartout-iOS/Global/Theme.swift @@ -191,7 +191,7 @@ extension MFMailComposeViewController { // FIXME: load from index JSON extension Infrastructure.Metadata { - var logo: UIImage? { + var logo: UIImage { return ImageAsset(name: name.lowercased()).image } } diff --git a/Passepartout-iOS/Providers.xcassets/placeholder.imageset/Contents.json b/Passepartout-iOS/Providers.xcassets/placeholder.imageset/Contents.json new file mode 100644 index 00000000..13f500e4 --- /dev/null +++ b/Passepartout-iOS/Providers.xcassets/placeholder.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "placeholder@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "placeholder@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Passepartout-iOS/Providers.xcassets/placeholder.imageset/placeholder@2x.png b/Passepartout-iOS/Providers.xcassets/placeholder.imageset/placeholder@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3117a66223f054a915dc42dc25c60d7b80ccbe48 GIT binary patch literal 2362 zcmZ`*X;jjS690pOfPuMnUCOQ0v`i4MTY3}11r$qj&k%7B*Tg*)z2uULxZLu(rsj32 z#HGw76Ror|w+u5w#g?nytT10{+RaBF--mbR%=~8N%x~tL`7md4PCMBl5h@4(0FXp` zf{Pd}e<}c3#F@uoOeCF zKG2-rsWH1ztWl5fE|a;Bo0Q4OaBs|LckSLQUYftjxsHUO;=DP&zV+az@ zMySqHPd@>QVWNJq=pwx-gx}S$KwVVWGLACjP{I#(4s+k2 zf(gVDu!IW846>~aYRgI_A>^`*)oflLkey$DT>Q0&sHvyiC%t7P^3#|Pt{r_0>@XYB zJDiFA28%kk*x+iZ`!L5A%w$l7WEB`Ci+k-P**?X?@|Ku|pkhcD>cerG=QssY`YKhu zL$`pSDN>&$Pg=}00T_XL!hlcci zATflyqF8k&XB8#X?rnAGk-Hq!ea!*9qX;2 z;6iziZB3ohg|J-_sleqt<98F1~=J^}n$V z@1^q|%veJf52GwMt$={s!QM9i@I&n@te{OYs_;@p%9^KLOnkgs!hGjkZHYN>ixw20UR(YQC5)DTc<5L5 zC}rD^>hZ2Qon>s_cGT?&*+pbIScLFbI`>re3<6k)_S{e8!)NgsH8feS>unX;OqNfq z)VDnAANy2GJqI;E&ciNVQG@Jr5@;@Vv$CXWkhPpFW-F(NwL}Zp`U5=7ogvaTO9!1I z)FfUEt0#&VD?^)0+C*nE^q)*jGk(`osefz`w06V|5Oh6w__Yoe++6?%U0Qd$oZJ1_ z-$iv5lnUE_6+c}$tt4uy;vh9HS(9=O+AJ5P|ErV?k1Bg=uUFWo>HXl4=t+;yz*65$ z;&O<^y_Ye9Q^GbnIrW6e-i5}QOgDYGXGq=-)WEq~R_eFwEhU_pOLxyg4JK8nS=hpg z5>Gd77CfatTDp}?)7{*aQG(1LY@6c6LZ;Nm4@k^w!v$Ywtr?6{FkH2J(O>?|mjz|{ z(@MT=mSc$9+zc*ASz^`$ho)MSB!%n-S@y}bQF3&H_eW?KpS8fOQ`DL$J$ER| z+WI$-p@1)8YNz-yww2uugHUuswH$4_(toU!-Wdg_4of%YZ^TN2`t ze(loVVl`lR2b0lwwV0a+T1gfy|djngSmUmqMEm!b9DD6O9+O@YsTdvZs zKU5_NeMe)qeKH9(**-qBhxv<%6vL0(E_=EF!R(pNA&y$Kav8}KMlaDS_u?6z4Zf0X z?nt?N4`_{bxE$8v_;>%+$)3UGC@rPpU;q`0RKw7>6^2YA!`=ISSx||u}D6(u>MR*jg+op^Sh8m2j2ura| zOWH#|07dP#af+>C0&&zKK!`!fKV`pSmOhA^+ird@8%B9y`^axql)I<4bdP8R=fKx} zCeh!VQmFZPS5@enl6 zGw#3$s;>1twH2t1swAevO!z)XRMhU&ksybD-X~1oiSwh1kXhJ%zjX!Dy0oiNcc#(M zOyUJ{U1}3PZX;iD3jgs&(pG)`wf3YRMY?|3+>x+q|T_N$)EDcOV|yW`0A=}EFnE+&%{owG8jzv&ZM zs$?|x?sn824YN4ss3eTjplXg0{YyGm#qYjBUW!h|i{}$rxiCvQN11Bas+bU|8!3z5 z8*fGNMc=D(HanL@2?Q2}Nt73QZ#sC`TaWR%pc*~QM>XLR!QK(;UGrVv`-78uC|pn5 z=_s4yR5F}Ow!++stqmRdi(Z(t$cu-%R9Q&6~9mmU|XdAiFxXcdoF`a>jr(UeJws zwpFE(eFbozNlv}>d3b+Px#Zdy8(?kUWf5BTm%qpoCqy=YQ}#7#ds?1 literal 0 HcmV?d00001 diff --git a/Passepartout-iOS/Providers.xcassets/placeholder.imageset/placeholder@3x.png b/Passepartout-iOS/Providers.xcassets/placeholder.imageset/placeholder@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..1dc8aed71d220bc60830ad8a04efaa44ce362982 GIT binary patch literal 3468 zcmZ`+cRbXO|9_t|&OUp@8E12{$C(#$&N#b_sXwb_pe? z^7$Cy$_&-l=g;3CzsKwGd_7<5`FQ>H`s0;iYh%vNBFq8+0J{a+)c%ZJ{{w{a% zIa`>b9B(del>}vpIPneNIgC8)IpDg`Z0Ti;hDez4W{ErXU`#P&l1yAyxj3j_+beI| z%Y}dSYr<_5hDcLdinO`tbh1)`P9+ zt?jpj$?pv(&o^owZ1*j-|Gjlm95LB@D1st4;T(*TYe-0E;|l=R$YPqxrhoHJSuK1)xo03sKs* zIi*XNB1coE@zc~AXa5@5-wv-uXXdW4w;vJ(dXnf~vryrbeah2-=JDi5>gh7afmC_w z4RN{5xFnj4m$28MX zY3tU@M*eHI&Ie<*W28x@z=!^67m$`L1Yq`&A@s7Q+!h(C!p0*9;^3)Xkb>vrOH z4LUr2aC6sFVRRv=qM!(7x?r2z+&b*@>CWYKy;QY>?Zt(?!E>+fjsJ##Fp9m>#zuI7 z#;T>Xq{KVGA#&tpVZcX6$@$M^a)DK5OslFi5J9mIjBYI|f&MV0B2&VNcR(3*a~}9W zlltzE@;B%|IyBZ+UsbVex(3D=;!Cg#ls)BsSF&nRoN6F*_Xkgh|A*v%q8{7>iHq5l-XUT7`*2dStbyd86oMuarhTf{| zj-frecOw~nAE0b@3$&fA0+6LBd=D?yU>it!DL%k;(eb=Z(ww9={{+$%~CT^66(6P z#;}b#F;{TCVJF1MK`7Gv8 z=KV*fZt_#!gPV-!L$S@z#6yJY*0xH!+?B}%Z^r9R_&JoB8cYriQfX%9P2872ms)Es z4q^)LZ%^oyUu?JTOvRae;q%77**XWA@q&3e(3cEzyjiIJ#x39Q2m--{mJV zb}IuEmMPQWY1eSj%2&c(yzR+7+xN zdZe5;;fD&v5DzW)6sj0ZNj9END4_}ODy>ev)MSvqh2KCk31U#_r$Bs=ta<10Klpp2 z8ZCLQbWd$dFU|Vb^_1x~^c2%!%sotT^Og{M#TfLSvW#}FOzby$9gbj1pxyJGBG?5_ ze;L7rsEz|&%t~5;?oJX#XfpeAR6zJk`^#74&qS$x!4eD?KS{obV}6z8v#h(A@l5~e zZ&UDCSkvr-YHZESV%{b$)6a6kVo&*^VDpAR*5zhMYyf>;$S-u?+x!6(G|e|2C9J0kx3o8LJ(GroT9R^Uc3^nhDr}h_)I@m znswFI{u4$l*E2|6%~enxRWy^?w^vv@U#9zQD`NtT^l_)wWp;J}#Zi?La^ne%bH$pY7N=CT~pkS2F?SKS?FBUF05#-^zn;=Xpl zp2(^vyK~4hkoCM?Bfv7Qe{xAygrmd*u5?M5_WIU(ak>ARsv3bi);*74;OB)nHye(b#qEkW@PMG^mF77ey^14^XhtzlDD5b9BN(xH!?vjbx8946TL_GNcuP-2- z%G=%3YvbeWS_AHeCb{JFFr8rMgrBFP36A}La_;t+&)hL%{L!%Rm2nRu8^{Rh|s(%U<$G27@#(_>9Dsuviabymw2ARn$TuJqTM+O zdbmrGx6okC;1nM??iSy=3hy?nftLD6ihTG?a73O}UObl!pz7S>aImnHM=gK2=scw|W(}J#N z7fpvBMimK8W}zeFVYk+qZj&aorg_=kyuw@kQUB7oW!+z{Ll|2G6_2K_k5Q9#bzOUNp`S0|8T18faI^h zR(BXZw5DxD&T!YN!Bx8M$E`I=jD+#8h@mIfKD=uGd4b1f5*$@tuSV}cPNVbNi?MXm zdj8m3_9{wF_4f<=v~bq!Z~jw(SPtIEw;DG_(q29hUl?1}l+aD%@K^sZ6R(jf8W0u+ z-I#ptR6tS4+9J3gG8NNssY5V{MXzYQ;C-%Lr~e0=xjMzM>q(__r&5bf9rvcJDESl< za=&5g?(Gh|cv^)|cEv)@*Z@z2^7!Y00BsZ1@lq%ZtN+teP8*^f_AU6<;7F1oOA6eG z%&*uYkIJwN{4zqM_PZ46s3oTtos0?mDO^KqCplIH1%xWRD_M&cAUamj*+#(muIbi5 zIgt7bbr#!hlwzxGw+#6ir$-svG}F9>7;R+s&Kt*yTK@RqjQahLuhYv`!I*Q}habYl z@?<@)CTv3xCjPW_pJV#(C)04z7G==MUqFc-d&79XkM->7kS3U2Civh8zDVzI-!lT# zR8(~lswxOoRYz5Iq>2VoT|-et4XL7nbMKP;KZ4*;pFqFp|6c$F(UqPFg#O#$5b8&W o!iD<+QBhIKfgwQ=-Z;Fka%i}J(YC(u*$`l1W@FlL!Sn9_05H`kPXGV_ literal 0 HcmV?d00001 diff --git a/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift b/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift index f87c65a4..6ed2570a 100644 --- a/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift +++ b/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift @@ -473,9 +473,14 @@ extension OrganizerViewController { case .profile: let cell = Cells.setting.dequeue(from: tableView, for: indexPath) let rowProfile = profileKey(at: indexPath) - if rowProfile.context == .provider, let metadata = InfrastructureFactory.shared.metadata(forName: rowProfile.id) { - cell.imageView?.image = metadata.logo - cell.leftText = metadata.description + if rowProfile.context == .provider { + if let metadata = InfrastructureFactory.shared.metadata(forName: rowProfile.id) { + cell.imageView?.image = metadata.logo + cell.leftText = metadata.description + } else { + cell.imageView?.image = Asset.Providers.placeholder.image + cell.leftText = rowProfile.id + } } else { cell.imageView?.image = nil cell.leftText = rowProfile.id From 6376af63f3854fd460c0033d27f4215afa19ef31 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Thu, 28 Nov 2019 11:52:28 +0100 Subject: [PATCH 7/7] Update Core commit --- Submodules/Core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Submodules/Core b/Submodules/Core index 010fdccd..5e25f768 160000 --- a/Submodules/Core +++ b/Submodules/Core @@ -1 +1 @@ -Subproject commit 010fdccdf019f73f399f99fde2bc6bb1571b596b +Subproject commit 5e25f768fb66832c4a539e7ff6ba7fa80d918a12