From 3f493c78e973bbd86a73e324909667454962aecd Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Tue, 9 Jan 2024 16:23:25 +0100 Subject: [PATCH 01/36] feat(account): update Session.swift and ServiceStatus.swift --- IVPNClient/Enums/ApiResults/Session.swift | 1 + IVPNClient/Models/ServiceStatus.swift | 2 ++ 2 files changed, 3 insertions(+) diff --git a/IVPNClient/Enums/ApiResults/Session.swift b/IVPNClient/Enums/ApiResults/Session.swift index 0c3b6101a..ebb39eed1 100644 --- a/IVPNClient/Enums/ApiResults/Session.swift +++ b/IVPNClient/Enums/ApiResults/Session.swift @@ -34,6 +34,7 @@ struct Session: Decodable { let token: String? let vpnUsername: String? let vpnPassword: String? + let deviceName: String? let serviceStatus: ServiceStatus let wireguard: WireGuardResult? } diff --git a/IVPNClient/Models/ServiceStatus.swift b/IVPNClient/Models/ServiceStatus.swift index 6d78209cf..fdae2b11e 100644 --- a/IVPNClient/Models/ServiceStatus.swift +++ b/IVPNClient/Models/ServiceStatus.swift @@ -35,6 +35,7 @@ struct ServiceStatus: Codable { let upgradeToUrl: String? let paymentMethod: String? let capabilities: [String]? + let deviceManagement: Bool // MARK: - Initialize - @@ -48,6 +49,7 @@ struct ServiceStatus: Codable { upgradeToUrl = service?.upgradeToUrl ?? nil paymentMethod = service?.paymentMethod ?? nil capabilities = service?.capabilities ?? nil + deviceManagement = service?.deviceManagement ?? false } // MARK: - Methods - From 7d0d3b9f11bcd1ed176a12d4bc06bed2b47c118c Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Tue, 9 Jan 2024 17:23:54 +0100 Subject: [PATCH 02/36] feat(account): update SessionStatus.swift --- IVPNClient/Enums/ApiResults/SessionStatus.swift | 1 + IVPNClient/Managers/KeyChain.swift | 12 ++++++++++++ IVPNClient/Managers/SessionManager.swift | 1 + 3 files changed, 14 insertions(+) diff --git a/IVPNClient/Enums/ApiResults/SessionStatus.swift b/IVPNClient/Enums/ApiResults/SessionStatus.swift index 68a5f6324..6af241eca 100644 --- a/IVPNClient/Enums/ApiResults/SessionStatus.swift +++ b/IVPNClient/Enums/ApiResults/SessionStatus.swift @@ -25,6 +25,7 @@ import Foundation struct SessionStatus: Decodable { let status: Int + let deviceName: String? let serviceStatus: ServiceStatus var serviceActive: Bool { diff --git a/IVPNClient/Managers/KeyChain.swift b/IVPNClient/Managers/KeyChain.swift index b44d1b23d..6ad685889 100644 --- a/IVPNClient/Managers/KeyChain.swift +++ b/IVPNClient/Managers/KeyChain.swift @@ -37,6 +37,7 @@ class KeyChain { private static let sessionTokenKey = "session_token" private static let vpnUsernameKey = "vpn_username" private static let vpnPasswordKey = "vpn_password" + private static let deviceNameKey = "deviceName" static let bundle: Keychain = { return Keychain(service: "net.ivpn.clients.ios", accessGroup: "WQXXM75BYN.net.ivpn.IVPN-Client").accessibility(.whenPasscodeSetThisDeviceOnly) @@ -149,12 +150,22 @@ class KeyChain { sessionToken = session.token vpnUsername = session.vpnUsername vpnPassword = session.vpnPassword + deviceName = session.deviceName if let wireguardResult = session.wireguard, let ipAddress = wireguardResult.ipAddress { KeyChain.wgIpAddress = ipAddress } } + class var deviceName: String? { + get { + return KeyChain.bundle[deviceNameKey] + } + set { + KeyChain.bundle[deviceNameKey] = newValue + } + } + static func clearAll() { username = nil tempUsername = nil @@ -166,6 +177,7 @@ class KeyChain { sessionToken = nil vpnUsername = nil vpnPassword = nil + deviceName = nil } } diff --git a/IVPNClient/Managers/SessionManager.swift b/IVPNClient/Managers/SessionManager.swift index c84636b64..6d93ac45c 100644 --- a/IVPNClient/Managers/SessionManager.swift +++ b/IVPNClient/Managers/SessionManager.swift @@ -141,6 +141,7 @@ class SessionManager { switch result { case .success(let model): Application.shared.serviceStatus = model.serviceStatus + KeyChain.deviceName = model.deviceName NotificationCenter.default.post(name: Notification.Name.EvaluatePlanUpdate, object: nil) if model.serviceActive { From 26329a92484f21d814b6a7690c60ddcc75038085 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Tue, 9 Jan 2024 17:35:13 +0100 Subject: [PATCH 03/36] feat(account): update Main.storyboard --- IVPNClient/Scenes/Base.lproj/Main.storyboard | 85 +++++++++++++------- 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/IVPNClient/Scenes/Base.lproj/Main.storyboard b/IVPNClient/Scenes/Base.lproj/Main.storyboard index 4f04e5a7c..a6b3b0372 100644 --- a/IVPNClient/Scenes/Base.lproj/Main.storyboard +++ b/IVPNClient/Scenes/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -21,7 +21,7 @@ - + @@ -4563,7 +4587,7 @@ - + @@ -4572,7 +4596,7 @@ - + @@ -4599,7 +4623,7 @@ - + @@ -4608,7 +4632,7 @@ - + @@ -4648,6 +4672,9 @@ + + + From 58c2e6faae631b7ed6049fff741f6aa9fe93abe9 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Tue, 9 Jan 2024 17:36:26 +0100 Subject: [PATCH 04/36] feat(account): update AccountView.swift --- .../Scenes/AccountScreen/View/AccountView.swift | 8 ++++++++ IVPNClient/ViewModels/AccountViewModel.swift | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/IVPNClient/Scenes/AccountScreen/View/AccountView.swift b/IVPNClient/Scenes/AccountScreen/View/AccountView.swift index 9962c1b07..8053d532f 100644 --- a/IVPNClient/Scenes/AccountScreen/View/AccountView.swift +++ b/IVPNClient/Scenes/AccountScreen/View/AccountView.swift @@ -34,6 +34,9 @@ class AccountView: UITableView { @IBOutlet weak var activeUntilLabel: UILabel! @IBOutlet weak var logOutActionButton: UIButton! @IBOutlet weak var activeUntilCell: UITableViewCell! + @IBOutlet weak var deviceNameTitle: UILabel! + @IBOutlet weak var deviceName: UILabel! + @IBOutlet weak var header: UIView! // MARK: - Properties - @@ -48,6 +51,11 @@ class AccountView: UITableView { subscriptionLabel.text = viewModel.subscriptionText activeUntilLabel.text = viewModel.activeUntilText activeUntilCell.isHidden = Application.shared.serviceStatus.isLegacyAccount() + deviceName.text = viewModel.deviceName + deviceNameTitle.isHidden = !viewModel.showDeviceName + deviceName.isHidden = !viewModel.showDeviceName + let headerHeight = viewModel.showDeviceName ? 270 : 210 + header.frame = CGRect(x: 0, y: 0, width: Int(header.frame.width), height: headerHeight) } func initQRCode(viewModel: AccountViewModel) { diff --git a/IVPNClient/ViewModels/AccountViewModel.swift b/IVPNClient/ViewModels/AccountViewModel.swift index 621f2566c..68a65ac44 100644 --- a/IVPNClient/ViewModels/AccountViewModel.swift +++ b/IVPNClient/ViewModels/AccountViewModel.swift @@ -50,6 +50,18 @@ struct AccountViewModel { return serviceStatus.isActive ? serviceStatus.activeUntilString() : "No active subscription" } + var deviceManagement: Bool { + return serviceStatus.deviceManagement + } + + var deviceName: String? { + return KeyChain.deviceName + } + + var showDeviceName: Bool { + return deviceManagement && deviceName != nil + } + // MARK: - Initialize - init(serviceStatus: ServiceStatus, authentication: Authentication) { From 9f7da3bb16a4a928d22736b69c1ee70df873580a Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Wed, 10 Jan 2024 11:55:52 +0100 Subject: [PATCH 05/36] feat(account): update ErrorResultSessionNew.swift --- IVPNClient/Enums/ApiResults/ErrorResultSessionNew.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/IVPNClient/Enums/ApiResults/ErrorResultSessionNew.swift b/IVPNClient/Enums/ApiResults/ErrorResultSessionNew.swift index 2eacac24f..f4a02fea6 100644 --- a/IVPNClient/Enums/ApiResults/ErrorResultSessionNew.swift +++ b/IVPNClient/Enums/ApiResults/ErrorResultSessionNew.swift @@ -30,6 +30,8 @@ struct SessionLimitData: Decodable { let upgradeToPlan: String let upgradeToUrl: String let paymentMethod: String + let deviceManagement: Bool + let deviceManagementUrl: String } struct ErrorResultSessionNew: Decodable { From 3cc100b988fab46bfd63ac4819ab2d1943ab0d46 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Wed, 10 Jan 2024 11:56:34 +0100 Subject: [PATCH 06/36] refactor: update AccountViewController.swift --- IVPNClient/Scenes/AccountScreen/AccountViewController.swift | 4 +--- IVPNClient/Utilities/Extensions/UIViewController+Ext.swift | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/IVPNClient/Scenes/AccountScreen/AccountViewController.swift b/IVPNClient/Scenes/AccountScreen/AccountViewController.swift index 3c59126d7..995dd0da1 100644 --- a/IVPNClient/Scenes/AccountScreen/AccountViewController.swift +++ b/IVPNClient/Scenes/AccountScreen/AccountViewController.swift @@ -53,9 +53,7 @@ class AccountViewController: UITableViewController { } @IBAction func deleteAccount(_ sender: UIButton) { - if let url = URL(string: "https://www.ivpn.net/account/settings") { - UIApplication.shared.open(url, options: [:], completionHandler: nil) - } + openWebPageInBrowser("https://www.ivpn.net/account/settings") } @IBAction func addMoreTime(_ sender: Any) { diff --git a/IVPNClient/Utilities/Extensions/UIViewController+Ext.swift b/IVPNClient/Utilities/Extensions/UIViewController+Ext.swift index 3b35775fd..6745c6cdd 100644 --- a/IVPNClient/Utilities/Extensions/UIViewController+Ext.swift +++ b/IVPNClient/Utilities/Extensions/UIViewController+Ext.swift @@ -128,6 +128,12 @@ extension UIViewController { } } + func openWebPageInBrowser(_ stringURL: String) { + if let url = URL(string: stringURL) { + UIApplication.shared.open(url, options: [:], completionHandler: nil) + } + } + func showSubscriptionActivatedAlert(serviceStatus: ServiceStatus, completion: (() -> Void)? = nil) { showAlert( title: "Thank you!", From 7673a576e9fa71ac9194efe67093a89df6873251 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Wed, 10 Jan 2024 13:16:51 +0100 Subject: [PATCH 07/36] feat(account): update LoginViewController.swift --- .../ControlPanelViewController+Ext.swift | 133 ++++++++++++++--- .../Scenes/Signup/LoginViewController.swift | 134 ++++++++++++++---- 2 files changed, 220 insertions(+), 47 deletions(-) diff --git a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift index ecc123ff4..e5445b06f 100644 --- a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift +++ b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift @@ -170,20 +170,7 @@ extension ControlPanelViewController { } override func createSessionTooManySessions(error: Any?) { - if let error = error as? ErrorResultSessionNew { - if let data = error.data { - if data.upgradable { - NotificationCenter.default.addObserver(self, selector: #selector(newSession), name: Notification.Name.NewSession, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(forceNewSession), name: Notification.Name.ForceNewSession, object: nil) - UserDefaults.shared.set(data.limit, forKey: UserDefaults.Key.sessionsLimit) - UserDefaults.shared.set(data.upgradeToUrl, forKey: UserDefaults.Key.upgradeToUrl) - present(NavigationManager.getUpgradePlanViewController(), animated: true, completion: nil) - return - } - } - } - - showCreateSessionAlert(message: "You've reached the maximum number of connected devices") + showTooManySessionsAlert(error: error as? ErrorResultSessionNew) } override func createSessionAuthenticationError() { @@ -234,16 +221,116 @@ extension ControlPanelViewController { present(NavigationManager.getLoginViewController(), animated: true) } - func showCreateSessionAlert(message: String) { - showActionSheet(title: message, actions: ["Log out from all other devices", "Try again"], sourceView: self.controlPanelView.connectSwitch) { index in - switch index { - case 0: - self.sessionManager.createSession(force: true) - case 1: - self.sessionManager.createSession() - default: - break + private func showTooManySessionsAlert(error: ErrorResultSessionNew?) { + let message = "You've reached the maximum number of connected devices" + + // Default + guard let error = error, let data = error.data else { + showActionSheet(title: message, actions: [ + "Log out all devices", + "Retry" + ], sourceView: self.controlPanelView.connectSwitch) { [self] index in + switch index { + case 0: + forceNewSession() + case 1: + newSession() + default: + break + } + } + + return + } + + let service = ServiceType.getType(currentPlan: data.currentPlan) + + // Device Management enabled, Pro plan + if data.deviceManagement && service == .pro { + showActionSheet(title: message, actions: [ + "Log out all devices", + "Visit Device Management", + "Retry (I have removed the device)", + ], sourceView: self.controlPanelView.connectSwitch) { [self] index in + switch index { + case 0: + forceNewSession() + case 1: + openWebPageInBrowser(data.deviceManagementUrl) + case 2: + newSession() + default: + break + } + } + + return + } + + // Device Management disabled, Pro plan + if !data.deviceManagement && service == .pro { + showActionSheet(title: message, actions: [ + "Log out all devices", + "Enable Device Management" + ], sourceView: self.controlPanelView.connectSwitch) { [self] index in + switch index { + case 0: + forceNewSession() + case 1: + openWebPageInBrowser(data.deviceManagementUrl) + default: + break + } } + + return + } + + // Device Management enabled, Standard plan + if data.deviceManagement && service == .standard { + showActionSheet(title: message, actions: [ + "Log out all devices", + "Visit Device Management", + "Retry (I have removed the device)", + "Upgrade for 7 devices" + ], sourceView: self.controlPanelView.connectSwitch) { [self] index in + switch index { + case 0: + forceNewSession() + case 1: + openWebPageInBrowser(data.deviceManagementUrl) + case 2: + newSession() + case 3: + openWebPageInBrowser(data.upgradeToUrl) + default: + break + } + } + + return + } + + // Device Management disabled, Standard plan + if !data.deviceManagement && service == .standard { + showActionSheet(title: message, actions: [ + "Log out all devices", + "Enable Device Management", + "Upgrade for 7 devices" + ], sourceView: self.controlPanelView.connectSwitch) { [self] index in + switch index { + case 0: + forceNewSession() + case 1: + openWebPageInBrowser(data.deviceManagementUrl) + case 2: + openWebPageInBrowser(data.upgradeToUrl) + default: + break + } + } + + return } } diff --git a/IVPNClient/Scenes/Signup/LoginViewController.swift b/IVPNClient/Scenes/Signup/LoginViewController.swift index 14a3b5eac..27888ea59 100644 --- a/IVPNClient/Scenes/Signup/LoginViewController.swift +++ b/IVPNClient/Scenes/Signup/LoginViewController.swift @@ -311,21 +311,7 @@ extension LoginViewController { hud.dismiss() Application.shared.authentication.removeStoredCredentials() loginProcessStarted = false - - if let error = error as? ErrorResultSessionNew, let data = error.data { - if data.upgradable { - NotificationCenter.default.removeObserver(self, name: Notification.Name.NewSession, object: nil) - NotificationCenter.default.removeObserver(self, name: Notification.Name.ForceNewSession, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(newSession), name: Notification.Name.NewSession, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(forceNewSession), name: Notification.Name.ForceNewSession, object: nil) - UserDefaults.shared.set(data.limit, forKey: UserDefaults.Key.sessionsLimit) - UserDefaults.shared.set(data.upgradeToUrl, forKey: UserDefaults.Key.upgradeToUrl) - present(NavigationManager.getUpgradePlanViewController(), animated: true, completion: nil) - return - } - } - - showCreateSessionAlert(message: "You've reached the maximum number of connected devices") + showTooManySessionsAlert(error: error as? ErrorResultSessionNew) } override func createSessionAuthenticationError() { @@ -384,16 +370,116 @@ extension LoginViewController { presentCaptchaScreen(error: error) } - private func showCreateSessionAlert(message: String) { - showActionSheet(title: message, actions: ["Log out from all other devices", "Try again"], sourceView: self.userName) { [self] index in - switch index { - case 0: - forceNewSession() - case 1: - newSession() - default: - break + private func showTooManySessionsAlert(error: ErrorResultSessionNew?) { + let message = "You've reached the maximum number of connected devices" + + // Default + guard let error = error, let data = error.data else { + showActionSheet(title: message, actions: [ + "Log out all devices", + "Retry" + ], sourceView: self.userName) { [self] index in + switch index { + case 0: + forceNewSession() + case 1: + newSession() + default: + break + } + } + + return + } + + let service = ServiceType.getType(currentPlan: data.currentPlan) + + // Device Management enabled, Pro plan + if data.deviceManagement && service == .pro { + showActionSheet(title: message, actions: [ + "Log out all devices", + "Visit Device Management", + "Retry (I have removed the device)", + ], sourceView: self.userName) { [self] index in + switch index { + case 0: + forceNewSession() + case 1: + openWebPageInBrowser(data.deviceManagementUrl) + case 2: + newSession() + default: + break + } } + + return + } + + // Device Management disabled, Pro plan + if !data.deviceManagement && service == .pro { + showActionSheet(title: message, actions: [ + "Log out all devices", + "Enable Device Management" + ], sourceView: self.userName) { [self] index in + switch index { + case 0: + forceNewSession() + case 1: + openWebPageInBrowser(data.deviceManagementUrl) + default: + break + } + } + + return + } + + // Device Management enabled, Standard plan + if data.deviceManagement && service == .standard { + showActionSheet(title: message, actions: [ + "Log out all devices", + "Visit Device Management", + "Retry (I have removed the device)", + "Upgrade for 7 devices" + ], sourceView: self.userName) { [self] index in + switch index { + case 0: + forceNewSession() + case 1: + openWebPageInBrowser(data.deviceManagementUrl) + case 2: + newSession() + case 3: + openWebPageInBrowser(data.upgradeToUrl) + default: + break + } + } + + return + } + + // Device Management disabled, Standard plan + if !data.deviceManagement && service == .standard { + showActionSheet(title: message, actions: [ + "Log out all devices", + "Enable Device Management", + "Upgrade for 7 devices" + ], sourceView: self.userName) { [self] index in + switch index { + case 0: + forceNewSession() + case 1: + openWebPageInBrowser(data.deviceManagementUrl) + case 2: + openWebPageInBrowser(data.upgradeToUrl) + default: + break + } + } + + return } } From 618d9be41244bed8c01937ff4af2b3349d0f51cf Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Tue, 16 Jan 2024 20:15:28 +0100 Subject: [PATCH 08/36] feat(account): update LoginViewController.swift --- .../ControlPanelViewController+Ext.swift | 20 +++++++++---------- .../Scenes/Signup/LoginViewController.swift | 20 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift index e5445b06f..4bd3f0dce 100644 --- a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift +++ b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift @@ -227,9 +227,9 @@ extension ControlPanelViewController { // Default guard let error = error, let data = error.data else { showActionSheet(title: message, actions: [ - "Log out all devices", + "Log out from all devices", "Retry" - ], sourceView: self.controlPanelView.connectSwitch) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.controlPanelView.connectSwitch) { [self] index in switch index { case 0: forceNewSession() @@ -248,10 +248,10 @@ extension ControlPanelViewController { // Device Management enabled, Pro plan if data.deviceManagement && service == .pro { showActionSheet(title: message, actions: [ - "Log out all devices", + "Log out from all devices", "Visit Device Management", "Retry (I have removed the device)", - ], sourceView: self.controlPanelView.connectSwitch) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.controlPanelView.connectSwitch) { [self] index in switch index { case 0: forceNewSession() @@ -270,9 +270,9 @@ extension ControlPanelViewController { // Device Management disabled, Pro plan if !data.deviceManagement && service == .pro { showActionSheet(title: message, actions: [ - "Log out all devices", + "Log out from all devices", "Enable Device Management" - ], sourceView: self.controlPanelView.connectSwitch) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.controlPanelView.connectSwitch) { [self] index in switch index { case 0: forceNewSession() @@ -289,11 +289,11 @@ extension ControlPanelViewController { // Device Management enabled, Standard plan if data.deviceManagement && service == .standard { showActionSheet(title: message, actions: [ - "Log out all devices", + "Log out from all devices", "Visit Device Management", "Retry (I have removed the device)", "Upgrade for 7 devices" - ], sourceView: self.controlPanelView.connectSwitch) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.controlPanelView.connectSwitch) { [self] index in switch index { case 0: forceNewSession() @@ -314,10 +314,10 @@ extension ControlPanelViewController { // Device Management disabled, Standard plan if !data.deviceManagement && service == .standard { showActionSheet(title: message, actions: [ - "Log out all devices", + "Log out from all devices", "Enable Device Management", "Upgrade for 7 devices" - ], sourceView: self.controlPanelView.connectSwitch) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.controlPanelView.connectSwitch) { [self] index in switch index { case 0: forceNewSession() diff --git a/IVPNClient/Scenes/Signup/LoginViewController.swift b/IVPNClient/Scenes/Signup/LoginViewController.swift index 27888ea59..be66e32de 100644 --- a/IVPNClient/Scenes/Signup/LoginViewController.swift +++ b/IVPNClient/Scenes/Signup/LoginViewController.swift @@ -376,9 +376,9 @@ extension LoginViewController { // Default guard let error = error, let data = error.data else { showActionSheet(title: message, actions: [ - "Log out all devices", + "Log out from all devices", "Retry" - ], sourceView: self.userName) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in switch index { case 0: forceNewSession() @@ -397,10 +397,10 @@ extension LoginViewController { // Device Management enabled, Pro plan if data.deviceManagement && service == .pro { showActionSheet(title: message, actions: [ - "Log out all devices", + "Log out from all devices", "Visit Device Management", "Retry (I have removed the device)", - ], sourceView: self.userName) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in switch index { case 0: forceNewSession() @@ -419,9 +419,9 @@ extension LoginViewController { // Device Management disabled, Pro plan if !data.deviceManagement && service == .pro { showActionSheet(title: message, actions: [ - "Log out all devices", + "Log out from all devices", "Enable Device Management" - ], sourceView: self.userName) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in switch index { case 0: forceNewSession() @@ -438,11 +438,11 @@ extension LoginViewController { // Device Management enabled, Standard plan if data.deviceManagement && service == .standard { showActionSheet(title: message, actions: [ - "Log out all devices", + "Log out from all devices", "Visit Device Management", "Retry (I have removed the device)", "Upgrade for 7 devices" - ], sourceView: self.userName) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in switch index { case 0: forceNewSession() @@ -463,10 +463,10 @@ extension LoginViewController { // Device Management disabled, Standard plan if !data.deviceManagement && service == .standard { showActionSheet(title: message, actions: [ - "Log out all devices", + "Log out from all devices", "Enable Device Management", "Upgrade for 7 devices" - ], sourceView: self.userName) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in switch index { case 0: forceNewSession() From 75c781cb3771e2c5b375785d7804eca569eccd6b Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Wed, 17 Jan 2024 10:32:26 +0100 Subject: [PATCH 09/36] feat(account): update ControlPanelViewController+Ext.swift --- .../ControlPanel/ControlPanelViewController+Ext.swift | 8 +++++--- IVPNClient/Scenes/Signup/LoginViewController.swift | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift index 4bd3f0dce..080a87992 100644 --- a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift +++ b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift @@ -188,7 +188,9 @@ extension ControlPanelViewController { override func sessionStatusNotFound() { guard !UserDefaults.standard.bool(forKey: "-UITests") else { return } logOut(deleteSession: false) - present(NavigationManager.getLoginViewController(), animated: true) + showErrorAlert(title: "You are logged out", message: "You have been redirected to the login page to re-enter your credentials.") { [self] _ in + present(NavigationManager.getLoginViewController(), animated: true) + } } override func deleteSessionStart() { @@ -250,7 +252,7 @@ extension ControlPanelViewController { showActionSheet(title: message, actions: [ "Log out from all devices", "Visit Device Management", - "Retry (I have removed the device)", + "Retry", ], cancelAction: "Cancel login", sourceView: self.controlPanelView.connectSwitch) { [self] index in switch index { case 0: @@ -291,7 +293,7 @@ extension ControlPanelViewController { showActionSheet(title: message, actions: [ "Log out from all devices", "Visit Device Management", - "Retry (I have removed the device)", + "Retry", "Upgrade for 7 devices" ], cancelAction: "Cancel login", sourceView: self.controlPanelView.connectSwitch) { [self] index in switch index { diff --git a/IVPNClient/Scenes/Signup/LoginViewController.swift b/IVPNClient/Scenes/Signup/LoginViewController.swift index be66e32de..e7b88b966 100644 --- a/IVPNClient/Scenes/Signup/LoginViewController.swift +++ b/IVPNClient/Scenes/Signup/LoginViewController.swift @@ -399,7 +399,7 @@ extension LoginViewController { showActionSheet(title: message, actions: [ "Log out from all devices", "Visit Device Management", - "Retry (I have removed the device)", + "Retry", ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in switch index { case 0: @@ -440,7 +440,7 @@ extension LoginViewController { showActionSheet(title: message, actions: [ "Log out from all devices", "Visit Device Management", - "Retry (I have removed the device)", + "Retry", "Upgrade for 7 devices" ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in switch index { From 21dc5389762118f5e253740e637de422bad53e1d Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 18 Jan 2024 08:20:43 +0100 Subject: [PATCH 10/36] feat(account): update LoginViewController --- IVPNClient/Managers/NavigationManager.swift | 11 ++++++++--- .../ControlPanel/ControlPanelViewController+Ext.swift | 4 +--- IVPNClient/Scenes/Signup/LoginViewController.swift | 9 +++++++++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/IVPNClient/Managers/NavigationManager.swift b/IVPNClient/Managers/NavigationManager.swift index f0ac8fdab..932a03a8c 100644 --- a/IVPNClient/Managers/NavigationManager.swift +++ b/IVPNClient/Managers/NavigationManager.swift @@ -35,11 +35,16 @@ class NavigationManager { return viewController } - static func getLoginViewController() -> UIViewController { + static func getLoginViewController(showLogoutAlert: Bool = false) -> UIViewController { let storyBoard = UIStoryboard(name: "Signup", bundle: nil) - let viewController = storyBoard.instantiateViewController(withIdentifier: "loginView") + let navController = storyBoard.instantiateViewController(withIdentifier: "loginView") as? UINavigationController + navController?.modalPresentationStyle = .formSheet - return viewController + if let viewController = navController?.topViewController as? LoginViewController { + viewController.showLogoutAlert = showLogoutAlert + } + + return navController! } static func getChangePlanViewController() -> UIViewController { diff --git a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift index 080a87992..1b3be6942 100644 --- a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift +++ b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift @@ -188,9 +188,7 @@ extension ControlPanelViewController { override func sessionStatusNotFound() { guard !UserDefaults.standard.bool(forKey: "-UITests") else { return } logOut(deleteSession: false) - showErrorAlert(title: "You are logged out", message: "You have been redirected to the login page to re-enter your credentials.") { [self] _ in - present(NavigationManager.getLoginViewController(), animated: true) - } + present(NavigationManager.getLoginViewController(showLogoutAlert: true), animated: true) } override func deleteSessionStart() { diff --git a/IVPNClient/Scenes/Signup/LoginViewController.swift b/IVPNClient/Scenes/Signup/LoginViewController.swift index e7b88b966..420305548 100644 --- a/IVPNClient/Scenes/Signup/LoginViewController.swift +++ b/IVPNClient/Scenes/Signup/LoginViewController.swift @@ -43,6 +43,8 @@ class LoginViewController: UIViewController { // MARK: - Properties - + var showLogoutAlert: Bool = false + private lazy var sessionManager: SessionManager = { let sessionManager = SessionManager() sessionManager.delegate = self @@ -136,6 +138,13 @@ class LoginViewController: UIViewController { navigationController?.navigationBar.setNeedsLayout() } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + if showLogoutAlert { + showErrorAlert(title: "You are logged out", message: "You have been redirected to the login page to re-enter your credentials.") + } + } + // MARK: - Observers - func addObservers() { From fbeb558d36e963344515198540cc9ece33bcb330 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 18 Jan 2024 08:23:21 +0100 Subject: [PATCH 11/36] feat(account): update ControlPanelViewController+Ext.swift --- .../ControlPanel/ControlPanelViewController+Ext.swift | 8 +++++++- IVPNClient/Scenes/Signup/LoginViewController.swift | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift index 1b3be6942..c101480bf 100644 --- a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift +++ b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift @@ -271,13 +271,16 @@ extension ControlPanelViewController { if !data.deviceManagement && service == .pro { showActionSheet(title: message, actions: [ "Log out from all devices", - "Enable Device Management" + "Enable Device Management", + "Retry", ], cancelAction: "Cancel login", sourceView: self.controlPanelView.connectSwitch) { [self] index in switch index { case 0: forceNewSession() case 1: openWebPageInBrowser(data.deviceManagementUrl) + case 2: + newSession() default: break } @@ -316,6 +319,7 @@ extension ControlPanelViewController { showActionSheet(title: message, actions: [ "Log out from all devices", "Enable Device Management", + "Retry", "Upgrade for 7 devices" ], cancelAction: "Cancel login", sourceView: self.controlPanelView.connectSwitch) { [self] index in switch index { @@ -324,6 +328,8 @@ extension ControlPanelViewController { case 1: openWebPageInBrowser(data.deviceManagementUrl) case 2: + newSession() + case 3: openWebPageInBrowser(data.upgradeToUrl) default: break diff --git a/IVPNClient/Scenes/Signup/LoginViewController.swift b/IVPNClient/Scenes/Signup/LoginViewController.swift index 420305548..91d74523f 100644 --- a/IVPNClient/Scenes/Signup/LoginViewController.swift +++ b/IVPNClient/Scenes/Signup/LoginViewController.swift @@ -429,13 +429,16 @@ extension LoginViewController { if !data.deviceManagement && service == .pro { showActionSheet(title: message, actions: [ "Log out from all devices", - "Enable Device Management" + "Enable Device Management", + "Retry", ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in switch index { case 0: forceNewSession() case 1: openWebPageInBrowser(data.deviceManagementUrl) + case 2: + newSession() default: break } @@ -474,6 +477,7 @@ extension LoginViewController { showActionSheet(title: message, actions: [ "Log out from all devices", "Enable Device Management", + "Retry", "Upgrade for 7 devices" ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in switch index { @@ -482,6 +486,8 @@ extension LoginViewController { case 1: openWebPageInBrowser(data.deviceManagementUrl) case 2: + newSession() + case 3: openWebPageInBrowser(data.upgradeToUrl) default: break From d1f61191536ee1fd3711889aa554d7ae9b7e0954 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 18 Jan 2024 08:31:53 +0100 Subject: [PATCH 12/36] feat(account): update LoginViewController --- .../ControlPanel/ControlPanelViewController+Ext.swift | 11 ++++++----- IVPNClient/Scenes/Signup/LoginViewController.swift | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift index c101480bf..3f3465381 100644 --- a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift +++ b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift @@ -225,7 +225,7 @@ extension ControlPanelViewController { let message = "You've reached the maximum number of connected devices" // Default - guard let error = error, let data = error.data else { + guard let error = error, let data = error.data, data.paymentMethod == "prepaid" else { showActionSheet(title: message, actions: [ "Log out from all devices", "Retry" @@ -244,9 +244,10 @@ extension ControlPanelViewController { } let service = ServiceType.getType(currentPlan: data.currentPlan) + let deviceManagement = data.deviceManagement // Device Management enabled, Pro plan - if data.deviceManagement && service == .pro { + if deviceManagement && service == .pro { showActionSheet(title: message, actions: [ "Log out from all devices", "Visit Device Management", @@ -268,7 +269,7 @@ extension ControlPanelViewController { } // Device Management disabled, Pro plan - if !data.deviceManagement && service == .pro { + if !deviceManagement && service == .pro { showActionSheet(title: message, actions: [ "Log out from all devices", "Enable Device Management", @@ -290,7 +291,7 @@ extension ControlPanelViewController { } // Device Management enabled, Standard plan - if data.deviceManagement && service == .standard { + if deviceManagement && service == .standard { showActionSheet(title: message, actions: [ "Log out from all devices", "Visit Device Management", @@ -315,7 +316,7 @@ extension ControlPanelViewController { } // Device Management disabled, Standard plan - if !data.deviceManagement && service == .standard { + if !deviceManagement && service == .standard { showActionSheet(title: message, actions: [ "Log out from all devices", "Enable Device Management", diff --git a/IVPNClient/Scenes/Signup/LoginViewController.swift b/IVPNClient/Scenes/Signup/LoginViewController.swift index 91d74523f..1b118e02b 100644 --- a/IVPNClient/Scenes/Signup/LoginViewController.swift +++ b/IVPNClient/Scenes/Signup/LoginViewController.swift @@ -383,7 +383,7 @@ extension LoginViewController { let message = "You've reached the maximum number of connected devices" // Default - guard let error = error, let data = error.data else { + guard let error = error, let data = error.data, data.paymentMethod == "prepaid" else { showActionSheet(title: message, actions: [ "Log out from all devices", "Retry" @@ -402,9 +402,10 @@ extension LoginViewController { } let service = ServiceType.getType(currentPlan: data.currentPlan) + let deviceManagement = data.deviceManagement // Device Management enabled, Pro plan - if data.deviceManagement && service == .pro { + if deviceManagement && service == .pro { showActionSheet(title: message, actions: [ "Log out from all devices", "Visit Device Management", @@ -426,7 +427,7 @@ extension LoginViewController { } // Device Management disabled, Pro plan - if !data.deviceManagement && service == .pro { + if !deviceManagement && service == .pro { showActionSheet(title: message, actions: [ "Log out from all devices", "Enable Device Management", @@ -448,7 +449,7 @@ extension LoginViewController { } // Device Management enabled, Standard plan - if data.deviceManagement && service == .standard { + if deviceManagement && service == .standard { showActionSheet(title: message, actions: [ "Log out from all devices", "Visit Device Management", @@ -473,7 +474,7 @@ extension LoginViewController { } // Device Management disabled, Standard plan - if !data.deviceManagement && service == .standard { + if !deviceManagement && service == .standard { showActionSheet(title: message, actions: [ "Log out from all devices", "Enable Device Management", From 4d47406921958f004dd38e8e8929d171cee2ab7e Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 18 Jan 2024 12:40:48 +0100 Subject: [PATCH 13/36] feat(account): update Alerts+Ext.swift --- IVPNClient/Scenes/Signup/LoginViewController.swift | 10 +++++----- IVPNClient/Utilities/Extensions/Alerts+Ext.swift | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/IVPNClient/Scenes/Signup/LoginViewController.swift b/IVPNClient/Scenes/Signup/LoginViewController.swift index 1b118e02b..51b171691 100644 --- a/IVPNClient/Scenes/Signup/LoginViewController.swift +++ b/IVPNClient/Scenes/Signup/LoginViewController.swift @@ -387,7 +387,7 @@ extension LoginViewController { showActionSheet(title: message, actions: [ "Log out from all devices", "Retry" - ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.userName, permittedArrowDirections: [.up]) { [self] index in switch index { case 0: forceNewSession() @@ -410,7 +410,7 @@ extension LoginViewController { "Log out from all devices", "Visit Device Management", "Retry", - ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.userName, permittedArrowDirections: [.up]) { [self] index in switch index { case 0: forceNewSession() @@ -432,7 +432,7 @@ extension LoginViewController { "Log out from all devices", "Enable Device Management", "Retry", - ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.userName, permittedArrowDirections: [.up]) { [self] index in switch index { case 0: forceNewSession() @@ -455,7 +455,7 @@ extension LoginViewController { "Visit Device Management", "Retry", "Upgrade for 7 devices" - ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.userName, permittedArrowDirections: [.up]) { [self] index in switch index { case 0: forceNewSession() @@ -480,7 +480,7 @@ extension LoginViewController { "Enable Device Management", "Retry", "Upgrade for 7 devices" - ], cancelAction: "Cancel login", sourceView: self.userName) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.userName, permittedArrowDirections: [.up]) { [self] index in switch index { case 0: forceNewSession() diff --git a/IVPNClient/Utilities/Extensions/Alerts+Ext.swift b/IVPNClient/Utilities/Extensions/Alerts+Ext.swift index c851f6922..8f75d66af 100644 --- a/IVPNClient/Utilities/Extensions/Alerts+Ext.swift +++ b/IVPNClient/Utilities/Extensions/Alerts+Ext.swift @@ -46,7 +46,7 @@ extension UIViewController { present(alert, animated: true, completion: nil) } - func showActionSheet(image: UIImage? = nil, selected: String? = nil, largeText: Bool = false, centered: Bool = false, title message: String = "", actions: [String] = [], cancelAction: String = "Cancel", sourceView: UIView = UIView(), disableDismiss: Bool = false, completion: @escaping (_ index: Int) -> Void) { + func showActionSheet(image: UIImage? = nil, selected: String? = nil, largeText: Bool = false, centered: Bool = false, title message: String = "", actions: [String] = [], cancelAction: String = "Cancel", sourceView: UIView = UIView(), disableDismiss: Bool = false, permittedArrowDirections: UIPopoverArrowDirection = [.any], completion: @escaping (_ index: Int) -> Void) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet) let messageFont: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: largeText ? 17 : 16)] @@ -79,6 +79,7 @@ extension UIViewController { if let popoverController = alert.popoverPresentationController { popoverController.sourceView = sourceView popoverController.sourceRect = sourceView.bounds + popoverController.permittedArrowDirections = permittedArrowDirections if centered { popoverController.permittedArrowDirections = [] } From e9111899f8e7860155172c63dce79f9f51656240 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Fri, 19 Jan 2024 14:07:56 +0100 Subject: [PATCH 14/36] feat(account): update LoginViewController --- IVPNClient/Scenes/Signup/LoginViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IVPNClient/Scenes/Signup/LoginViewController.swift b/IVPNClient/Scenes/Signup/LoginViewController.swift index 51b171691..3472e28e4 100644 --- a/IVPNClient/Scenes/Signup/LoginViewController.swift +++ b/IVPNClient/Scenes/Signup/LoginViewController.swift @@ -454,7 +454,7 @@ extension LoginViewController { "Log out from all devices", "Visit Device Management", "Retry", - "Upgrade for 7 devices" + "Switch to IVPN Pro" ], cancelAction: "Cancel login", sourceView: self.userName, permittedArrowDirections: [.up]) { [self] index in switch index { case 0: @@ -479,7 +479,7 @@ extension LoginViewController { "Log out from all devices", "Enable Device Management", "Retry", - "Upgrade for 7 devices" + "Switch to IVPN Pro" ], cancelAction: "Cancel login", sourceView: self.userName, permittedArrowDirections: [.up]) { [self] index in switch index { case 0: From 8fac5f935aecde1d3c8a10da7fc444c0cfef82d2 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Mon, 22 Jan 2024 10:42:17 +0100 Subject: [PATCH 15/36] feat(account): update LoginViewController --- .../ControlPanelViewController+Ext.swift | 28 +++++++++++++++++-- .../Scenes/Signup/LoginViewController.swift | 26 +++++++++++++++-- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift index 3f3465381..d4688999f 100644 --- a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift +++ b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift @@ -224,12 +224,12 @@ extension ControlPanelViewController { private func showTooManySessionsAlert(error: ErrorResultSessionNew?) { let message = "You've reached the maximum number of connected devices" - // Default - guard let error = error, let data = error.data, data.paymentMethod == "prepaid" else { + // Legacy account, Pro plan + guard let error = error, let data = error.data else { showActionSheet(title: message, actions: [ "Log out from all devices", "Retry" - ], cancelAction: "Cancel login", sourceView: self.controlPanelView.connectSwitch) { [self] index in + ], cancelAction: "Cancel login", sourceView: self.controlPanelView.connectSwitch, permittedArrowDirections: [.up]) { [self] index in switch index { case 0: forceNewSession() @@ -243,6 +243,28 @@ extension ControlPanelViewController { return } + // Legacy account, Standard plan + guard data.paymentMethod == "prepaid" else { + showActionSheet(title: message, actions: [ + "Log out from all devices", + "Retry", + "Switch to IVPN Pro" + ], cancelAction: "Cancel login", sourceView: self.controlPanelView.connectSwitch, permittedArrowDirections: [.up]) { [self] index in + switch index { + case 0: + forceNewSession() + case 1: + newSession() + case 2: + openWebPageInBrowser(data.upgradeToUrl) + default: + break + } + } + + return + } + let service = ServiceType.getType(currentPlan: data.currentPlan) let deviceManagement = data.deviceManagement diff --git a/IVPNClient/Scenes/Signup/LoginViewController.swift b/IVPNClient/Scenes/Signup/LoginViewController.swift index 3472e28e4..02c649327 100644 --- a/IVPNClient/Scenes/Signup/LoginViewController.swift +++ b/IVPNClient/Scenes/Signup/LoginViewController.swift @@ -382,8 +382,8 @@ extension LoginViewController { private func showTooManySessionsAlert(error: ErrorResultSessionNew?) { let message = "You've reached the maximum number of connected devices" - // Default - guard let error = error, let data = error.data, data.paymentMethod == "prepaid" else { + // Legacy account, Pro plan + guard let error = error, let data = error.data else { showActionSheet(title: message, actions: [ "Log out from all devices", "Retry" @@ -401,6 +401,28 @@ extension LoginViewController { return } + // Legacy account, Standard plan + guard data.paymentMethod == "prepaid" else { + showActionSheet(title: message, actions: [ + "Log out from all devices", + "Retry", + "Switch to IVPN Pro" + ], cancelAction: "Cancel login", sourceView: self.userName, permittedArrowDirections: [.up]) { [self] index in + switch index { + case 0: + forceNewSession() + case 1: + newSession() + case 2: + openWebPageInBrowser(data.upgradeToUrl) + default: + break + } + } + + return + } + let service = ServiceType.getType(currentPlan: data.currentPlan) let deviceManagement = data.deviceManagement From 1ab9388a5cfe270410ea6b3bf83d6ac62ffd89ff Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Mon, 22 Jan 2024 12:51:54 +0100 Subject: [PATCH 16/36] feat(account): update AccountViewController --- IVPNClient/Scenes/AccountScreen/AccountViewController.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/IVPNClient/Scenes/AccountScreen/AccountViewController.swift b/IVPNClient/Scenes/AccountScreen/AccountViewController.swift index 995dd0da1..e9ae06050 100644 --- a/IVPNClient/Scenes/AccountScreen/AccountViewController.swift +++ b/IVPNClient/Scenes/AccountScreen/AccountViewController.swift @@ -90,6 +90,7 @@ class AccountViewController: UITableViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) accountView.initQRCode(viewModel: viewModel) + sessionManager.getSessionStatus() } // MARK: - Observers - @@ -194,6 +195,11 @@ extension AccountViewController { } } + override func sessionStatusSuccess() { + let viewModel = AccountViewModel(serviceStatus: Application.shared.serviceStatus, authentication: Application.shared.authentication) + accountView.setupView(viewModel: viewModel) + } + } // MARK: - JGProgressHUDDelegate - From b40989fa49eb7e5148dfe42766e22c0680ced637 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Mon, 22 Jan 2024 13:04:08 +0100 Subject: [PATCH 17/36] feat(account): update AccountViewController --- .../Scenes/AccountScreen/AccountViewController.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/IVPNClient/Scenes/AccountScreen/AccountViewController.swift b/IVPNClient/Scenes/AccountScreen/AccountViewController.swift index e9ae06050..507f5ead5 100644 --- a/IVPNClient/Scenes/AccountScreen/AccountViewController.swift +++ b/IVPNClient/Scenes/AccountScreen/AccountViewController.swift @@ -97,6 +97,7 @@ class AccountViewController: UITableViewController { func addObservers() { NotificationCenter.default.addObserver(self, selector: #selector(subscriptionActivated), name: Notification.Name.SubscriptionActivated, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(subscriptionActivated), name: Notification.Name.ServiceAuthorized, object: nil) } // MARK: - Private methods - @@ -196,8 +197,12 @@ extension AccountViewController { } override func sessionStatusSuccess() { - let viewModel = AccountViewModel(serviceStatus: Application.shared.serviceStatus, authentication: Application.shared.authentication) - accountView.setupView(viewModel: viewModel) + subscriptionActivated() + } + + override func sessionStatusNotFound() { + logOut(deleteSession: false) + present(NavigationManager.getLoginViewController(showLogoutAlert: true), animated: true) } } From 25d6ca451e1d71e28bcfff1a959be14708f89942 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 25 Jan 2024 10:20:05 +0100 Subject: [PATCH 18/36] feat(account): update AccountView.swift --- IVPNClient/Scenes/AccountScreen/View/AccountView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/IVPNClient/Scenes/AccountScreen/View/AccountView.swift b/IVPNClient/Scenes/AccountScreen/View/AccountView.swift index 8053d532f..a7e7d5066 100644 --- a/IVPNClient/Scenes/AccountScreen/View/AccountView.swift +++ b/IVPNClient/Scenes/AccountScreen/View/AccountView.swift @@ -56,6 +56,8 @@ class AccountView: UITableView { deviceName.isHidden = !viewModel.showDeviceName let headerHeight = viewModel.showDeviceName ? 270 : 210 header.frame = CGRect(x: 0, y: 0, width: Int(header.frame.width), height: headerHeight) + reloadData() + layoutIfNeeded() } func initQRCode(viewModel: AccountViewModel) { From e4a58ab42699fa13260a55428fd1da75fad3856b Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Mon, 29 Jan 2024 13:37:17 +0100 Subject: [PATCH 19/36] fix(ikev2): use selected host as exit server ip --- IVPNClient/Managers/ConnectionManager.swift | 10 ++++++---- IVPNClient/Managers/VPNManager.swift | 11 +++++------ IVPNClient/Models/AccessDetails.swift | 10 +++++----- IVPNClient/Models/VPNServer.swift | 4 +--- IVPNClient/Models/VPNServerList.swift | 14 +------------- .../Scenes/MainScreen/Map/MapScrollView.swift | 2 +- .../Extensions/NETunnelProviderProtocol+Ext.swift | 2 +- IVPNClient/Utilities/Pinger/Pinger.swift | 2 +- 8 files changed, 21 insertions(+), 34 deletions(-) diff --git a/IVPNClient/Managers/ConnectionManager.swift b/IVPNClient/Managers/ConnectionManager.swift index 4cddb2792..be4ed03d2 100644 --- a/IVPNClient/Managers/ConnectionManager.swift +++ b/IVPNClient/Managers/ConnectionManager.swift @@ -239,9 +239,10 @@ class ConnectionManager { func connect() { updateSelectedServer(status: .connecting) + let host = NETunnelProviderProtocol.getHost() let accessDetails = AccessDetails( - serverAddress: settings.selectedServer.gateway, - ipAddresses: settings.selectedServer.ipAddresses, + ipAddress: host?.host ?? settings.selectedServer.gateway, + gateway: settings.selectedServer.gateway, username: KeyChain.vpnUsername ?? "", passwordRef: KeyChain.vpnPasswordRef ) @@ -309,9 +310,10 @@ class ConnectionManager { return } + let host = NETunnelProviderProtocol.getHost() let accessDetails = AccessDetails( - serverAddress: Application.shared.settings.selectedServer.gateway, - ipAddresses: Application.shared.settings.selectedServer.ipAddresses, + ipAddress: host?.host ?? settings.selectedServer.gateway, + gateway: settings.selectedServer.gateway, username: KeyChain.vpnUsername ?? "", passwordRef: KeyChain.vpnPasswordRef ) diff --git a/IVPNClient/Managers/VPNManager.swift b/IVPNClient/Managers/VPNManager.swift index c2cfc37cd..08d39beaa 100644 --- a/IVPNClient/Managers/VPNManager.swift +++ b/IVPNClient/Managers/VPNManager.swift @@ -126,8 +126,7 @@ class VPNManager { } private func setupNEVPNManager(manager: NEVPNManager, accessDetails: AccessDetails, status: NEVPNStatus? = nil, completion: @escaping (Error?) -> Void) { - let serverAddress = accessDetails.ipAddresses.randomElement() ?? accessDetails.serverAddress - self.setupIKEv2Tunnel(manager: manager, accessDetails: accessDetails, serverAddress: serverAddress, status: status) + self.setupIKEv2Tunnel(manager: manager, accessDetails: accessDetails, status: status) manager.saveToPreferences { error in if let error = error, error.code == 5 { manager.isOnDemandEnabled = false @@ -141,7 +140,7 @@ class VPNManager { } manager.loadFromPreferences { _ in - self.setupIKEv2Tunnel(manager: manager, accessDetails: accessDetails, serverAddress: serverAddress, status: status) + self.setupIKEv2Tunnel(manager: manager, accessDetails: accessDetails, status: status) manager.saveToPreferences { _ in completion(nil) } @@ -179,11 +178,11 @@ class VPNManager { } } - private func setupIKEv2Tunnel(manager: NEVPNManager, accessDetails: AccessDetails, serverAddress: String, status: NEVPNStatus? = nil) { + private func setupIKEv2Tunnel(manager: NEVPNManager, accessDetails: AccessDetails, status: NEVPNStatus? = nil) { let configuration = NEVPNProtocolIKEv2() - configuration.remoteIdentifier = accessDetails.serverAddress + configuration.remoteIdentifier = accessDetails.gateway configuration.localIdentifier = accessDetails.username - configuration.serverAddress = serverAddress + configuration.serverAddress = accessDetails.ipAddress configuration.username = accessDetails.username configuration.passwordReference = accessDetails.passwordRef configuration.authenticationMethod = .none diff --git a/IVPNClient/Models/AccessDetails.swift b/IVPNClient/Models/AccessDetails.swift index 1f494cba9..f6b0b7e03 100644 --- a/IVPNClient/Models/AccessDetails.swift +++ b/IVPNClient/Models/AccessDetails.swift @@ -25,14 +25,14 @@ import Foundation class AccessDetails { - var serverAddress: String - var ipAddresses: [String] + var ipAddress: String + var gateway: String var username: String var passwordRef: Data? - init(serverAddress: String, ipAddresses: [String], username: String, passwordRef: Data?) { - self.serverAddress = serverAddress - self.ipAddresses = ipAddresses + init(ipAddress: String, gateway: String, username: String, passwordRef: Data?) { + self.ipAddress = ipAddress + self.gateway = gateway self.username = username self.passwordRef = passwordRef } diff --git a/IVPNClient/Models/VPNServer.swift b/IVPNClient/Models/VPNServer.swift index 472209b59..e8ea0cea9 100644 --- a/IVPNClient/Models/VPNServer.swift +++ b/IVPNClient/Models/VPNServer.swift @@ -98,7 +98,6 @@ class VPNServer { private (set) var latitude: Double private (set) var longitude: Double private (set) var isp: String - private (set) var ipAddresses: [String] private (set) var hosts: [Host] private (set) var load: Double? private (set) var ipv6: IPv6? @@ -106,7 +105,7 @@ class VPNServer { // MARK: - Initialize - - init(gateway: String, dnsName: String? = nil, countryCode: String, country: String, city: String, latitude: Double = 0, longitude: Double = 0, isp: String = "", ipAddresses: [String] = [], hosts: [Host] = [], fastest: Bool = false, load: Double = 0, ipv6: IPv6? = nil) { + init(gateway: String, dnsName: String? = nil, countryCode: String, country: String, city: String, latitude: Double = 0, longitude: Double = 0, isp: String = "", hosts: [Host] = [], fastest: Bool = false, load: Double = 0, ipv6: IPv6? = nil) { self.gateway = gateway self.dnsName = dnsName self.countryCode = countryCode @@ -115,7 +114,6 @@ class VPNServer { self.latitude = latitude self.longitude = longitude self.isp = isp - self.ipAddresses = ipAddresses self.hosts = hosts self.fastest = fastest self.load = load diff --git a/IVPNClient/Models/VPNServerList.swift b/IVPNClient/Models/VPNServerList.swift index 3e819ef54..f8e09511e 100644 --- a/IVPNClient/Models/VPNServerList.swift +++ b/IVPNClient/Models/VPNServerList.swift @@ -282,7 +282,7 @@ class VPNServerList { } func getServer(byIpAddress ipAddress: String) -> VPNServer? { - return getServers().first { $0.ipAddresses.first { $0 == ipAddress } != nil } + return getServers().first { $0.hosts.first?.host == ipAddress } } func getServer(byGateway gateway: String) -> VPNServer? { @@ -394,21 +394,10 @@ class VPNServerList { servers.removeAll() for server in serversList { - var serverIpList = [String]() var serverHostsList = [Host]() - if let ipAddressList = server["ip_addresses"] as? [String?] { - for ipAddress in ipAddressList where ipAddress != nil { - serverIpList.append(ipAddress!) - } - } - if let hostsList = server["hosts"] as? [[String: Any]] { for host in hostsList { - if let hostIp = host["host"] as? String { - serverIpList.append(hostIp) - } - var newHost = Host( host: host["host"] as? String ?? "", hostName: host["hostname"] as? String ?? "", @@ -438,7 +427,6 @@ class VPNServerList { latitude: server["latitude"] as? Double ?? 0, longitude: server["longitude"] as? Double ?? 0, isp: server["isp"] as? String ?? "", - ipAddresses: serverIpList, hosts: serverHostsList ) diff --git a/IVPNClient/Scenes/MainScreen/Map/MapScrollView.swift b/IVPNClient/Scenes/MainScreen/Map/MapScrollView.swift index 17b24b598..fa8253147 100644 --- a/IVPNClient/Scenes/MainScreen/Map/MapScrollView.swift +++ b/IVPNClient/Scenes/MainScreen/Map/MapScrollView.swift @@ -222,7 +222,7 @@ class MapScrollView: UIScrollView { updateMapPosition(latitude: server.latitude, longitude: server.longitude, animated: animated, isLocalPosition: false) markerLocalView.hide(animated: animated) DispatchQueue.delay(0.25) { - let model = GeoLookup(ipAddress: server.ipAddresses.first ?? "", countryCode: server.countryCode, country: server.country, city: server.city, isIvpnServer: true, isp: "", latitude: server.latitude, longitude: server.longitude) + let model = GeoLookup(ipAddress: server.hosts.first?.host ?? "", countryCode: server.countryCode, country: server.country, city: server.city, isIvpnServer: true, isp: "", latitude: server.latitude, longitude: server.longitude) self.markerGatewayView.viewModel = ProofsViewModel(model: model) self.markerGatewayView.show(animated: animated) self.markerLocalView.hide(animated: false) diff --git a/IVPNClient/Utilities/Extensions/NETunnelProviderProtocol+Ext.swift b/IVPNClient/Utilities/Extensions/NETunnelProviderProtocol+Ext.swift index 7c87819db..11a60eca9 100644 --- a/IVPNClient/Utilities/Extensions/NETunnelProviderProtocol+Ext.swift +++ b/IVPNClient/Utilities/Extensions/NETunnelProviderProtocol+Ext.swift @@ -199,7 +199,7 @@ extension NETunnelProviderProtocol { // MARK: Methods - private static func getHost() -> Host? { + static func getHost() -> Host? { if let selectedHost = Application.shared.settings.selectedHost { return selectedHost } diff --git a/IVPNClient/Utilities/Pinger/Pinger.swift b/IVPNClient/Utilities/Pinger/Pinger.swift index 6a73331ae..a1d4a3011 100644 --- a/IVPNClient/Utilities/Pinger/Pinger.swift +++ b/IVPNClient/Utilities/Pinger/Pinger.swift @@ -48,7 +48,7 @@ class Pinger { UserDefaults.shared.set(Date().timeIntervalSince1970, forKey: "LastPingTimestamp") for server in serverList.getServers() { - if let ipAddress = server.ipAddresses.first { + if let ipAddress = server.hosts.first?.host { guard !ipAddress.isEmpty else { continue } let ping = Ping() ping.delegate = self From 88a9bacf6036b408b68a5b7bc1f5a5eb06faac05 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Tue, 30 Jan 2024 16:37:38 +0100 Subject: [PATCH 20/36] feat(account): update SessionManager.swift --- IVPNClient/Managers/SessionManager.swift | 5 +++-- IVPNClient/Models/ServiceStatus.swift | 10 +++++++++- .../ControlPanel/ControlPanelViewController+Ext.swift | 10 +++++----- IVPNClient/Scenes/Signup/LoginViewController.swift | 10 +++++----- .../Utilities/Extensions/UIViewController+Ext.swift | 2 +- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/IVPNClient/Managers/SessionManager.swift b/IVPNClient/Managers/SessionManager.swift index 6d93ac45c..0e3ffb320 100644 --- a/IVPNClient/Managers/SessionManager.swift +++ b/IVPNClient/Managers/SessionManager.swift @@ -27,7 +27,7 @@ import Foundation func createSessionStart() func createSessionSuccess() func createSessionFailure(error: Any?) - func createSessionTooManySessions(error: Any?) + func createSessionTooManySessions(error: Any?, isNewStyleAccount: Bool) func createSessionAuthenticationError() func createSessionServiceNotActive() func createSessionAccountNotActivated(error: Any?) @@ -67,6 +67,7 @@ class SessionManager { var kem = KEM() let params = sessionNewParams(force: force, username: username, confirmation: confirmation, captcha: captcha, captchaId: captchaId, kem: kem) + let isNewStyleAccount = ServiceStatus.isNewStyleAccount(username: username ?? "") let request = ApiRequestDI(method: .post, endpoint: Config.apiSessionNew, params: params) ApiService.shared.requestCustomError(request) { (result: ResultCustomError) in @@ -99,7 +100,7 @@ class SessionManager { return case 602: log(.info, message: "Create session error: createSessionTooManySessions") - self.delegate?.createSessionTooManySessions(error: error) + self.delegate?.createSessionTooManySessions(error: error, isNewStyleAccount: isNewStyleAccount) return case 11005: log(.info, message: "Create session error: createSessionAccountNotActivated") diff --git a/IVPNClient/Models/ServiceStatus.swift b/IVPNClient/Models/ServiceStatus.swift index fdae2b11e..34ab40996 100644 --- a/IVPNClient/Models/ServiceStatus.swift +++ b/IVPNClient/Models/ServiceStatus.swift @@ -91,7 +91,15 @@ struct ServiceStatus: Codable { } func isNewStyleAccount() -> Bool { - return paymentMethod == "prepaid" + guard let username = username else { + return true + } + + return username.hasPrefix("i-") + } + + static func isNewStyleAccount(username: String) -> Bool { + return username.hasPrefix("i-") } func daysUntilSubscriptionExpiration() -> Int { diff --git a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift index d4688999f..b21804fd4 100644 --- a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift +++ b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift @@ -169,8 +169,8 @@ extension ControlPanelViewController { connect() } - override func createSessionTooManySessions(error: Any?) { - showTooManySessionsAlert(error: error as? ErrorResultSessionNew) + override func createSessionTooManySessions(error: Any?, isNewStyleAccount: Bool) { + showTooManySessionsAlert(error: error as? ErrorResultSessionNew, isNewStyleAccount: isNewStyleAccount) } override func createSessionAuthenticationError() { @@ -221,11 +221,11 @@ extension ControlPanelViewController { present(NavigationManager.getLoginViewController(), animated: true) } - private func showTooManySessionsAlert(error: ErrorResultSessionNew?) { + private func showTooManySessionsAlert(error: ErrorResultSessionNew?, isNewStyleAccount: Bool) { let message = "You've reached the maximum number of connected devices" // Legacy account, Pro plan - guard let error = error, let data = error.data else { + guard let error = error, let data = error.data, data.upgradable else { showActionSheet(title: message, actions: [ "Log out from all devices", "Retry" @@ -244,7 +244,7 @@ extension ControlPanelViewController { } // Legacy account, Standard plan - guard data.paymentMethod == "prepaid" else { + guard isNewStyleAccount else { showActionSheet(title: message, actions: [ "Log out from all devices", "Retry", diff --git a/IVPNClient/Scenes/Signup/LoginViewController.swift b/IVPNClient/Scenes/Signup/LoginViewController.swift index 02c649327..0ca080ddf 100644 --- a/IVPNClient/Scenes/Signup/LoginViewController.swift +++ b/IVPNClient/Scenes/Signup/LoginViewController.swift @@ -316,11 +316,11 @@ extension LoginViewController { NotificationCenter.default.post(name: Notification.Name.UpdateFloatingPanelLayout, object: nil) } - override func createSessionTooManySessions(error: Any?) { + override func createSessionTooManySessions(error: Any?, isNewStyleAccount: Bool) { hud.dismiss() Application.shared.authentication.removeStoredCredentials() loginProcessStarted = false - showTooManySessionsAlert(error: error as? ErrorResultSessionNew) + showTooManySessionsAlert(error: error as? ErrorResultSessionNew, isNewStyleAccount: isNewStyleAccount) } override func createSessionAuthenticationError() { @@ -379,11 +379,11 @@ extension LoginViewController { presentCaptchaScreen(error: error) } - private func showTooManySessionsAlert(error: ErrorResultSessionNew?) { + private func showTooManySessionsAlert(error: ErrorResultSessionNew?, isNewStyleAccount: Bool) { let message = "You've reached the maximum number of connected devices" // Legacy account, Pro plan - guard let error = error, let data = error.data else { + guard let error = error, let data = error.data, data.upgradable else { showActionSheet(title: message, actions: [ "Log out from all devices", "Retry" @@ -402,7 +402,7 @@ extension LoginViewController { } // Legacy account, Standard plan - guard data.paymentMethod == "prepaid" else { + guard isNewStyleAccount else { showActionSheet(title: message, actions: [ "Log out from all devices", "Retry", diff --git a/IVPNClient/Utilities/Extensions/UIViewController+Ext.swift b/IVPNClient/Utilities/Extensions/UIViewController+Ext.swift index 6745c6cdd..71ca4b474 100644 --- a/IVPNClient/Utilities/Extensions/UIViewController+Ext.swift +++ b/IVPNClient/Utilities/Extensions/UIViewController+Ext.swift @@ -282,7 +282,7 @@ extension UIViewController: SessionManagerDelegate { func createSessionStart() {} func createSessionSuccess() {} func createSessionFailure(error: Any?) {} - func createSessionTooManySessions(error: Any?) {} + func createSessionTooManySessions(error: Any?, isNewStyleAccount: Bool) {} func createSessionAuthenticationError() {} func createSessionServiceNotActive() {} func createSessionAccountNotActivated(error: Any?) {} From 81f93cb6b00a5d873647e13f483b6e47e4b0e4fa Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Wed, 31 Jan 2024 16:16:09 +0100 Subject: [PATCH 21/36] feat(account): update LoginViewController --- .../ControlPanel/ControlPanelViewController+Ext.swift | 2 +- IVPNClient/Scenes/Signup/LoginViewController.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift index b21804fd4..15179d396 100644 --- a/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift +++ b/IVPNClient/Scenes/MainScreen/ControlPanel/ControlPanelViewController+Ext.swift @@ -225,7 +225,7 @@ extension ControlPanelViewController { let message = "You've reached the maximum number of connected devices" // Legacy account, Pro plan - guard let error = error, let data = error.data, data.upgradable else { + guard let error = error, let data = error.data, (isNewStyleAccount || data.upgradable) else { showActionSheet(title: message, actions: [ "Log out from all devices", "Retry" diff --git a/IVPNClient/Scenes/Signup/LoginViewController.swift b/IVPNClient/Scenes/Signup/LoginViewController.swift index 0ca080ddf..80c75fb43 100644 --- a/IVPNClient/Scenes/Signup/LoginViewController.swift +++ b/IVPNClient/Scenes/Signup/LoginViewController.swift @@ -383,7 +383,7 @@ extension LoginViewController { let message = "You've reached the maximum number of connected devices" // Legacy account, Pro plan - guard let error = error, let data = error.data, data.upgradable else { + guard let error = error, let data = error.data, (isNewStyleAccount || data.upgradable) else { showActionSheet(title: message, actions: [ "Log out from all devices", "Retry" From b2093abb28db3a91e417c0b6fef59b99a26f58cf Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 1 Feb 2024 12:15:05 +0100 Subject: [PATCH 22/36] refactor: update KemAlgorithm enum --- IVPNClient/Utilities/Kem/KemHelper.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IVPNClient/Utilities/Kem/KemHelper.swift b/IVPNClient/Utilities/Kem/KemHelper.swift index 584598d68..961e5a73e 100644 --- a/IVPNClient/Utilities/Kem/KemHelper.swift +++ b/IVPNClient/Utilities/Kem/KemHelper.swift @@ -25,7 +25,7 @@ import Foundation import CryptoKit enum KemAlgorithm: String, CaseIterable { - case Kyber1024 = "Kyber1024" + case Kyber1024 } enum KemHelperError: Error { From 925d23bede4d2cfda19f56160a6fbe3d501aa9af Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 1 Feb 2024 12:16:27 +0100 Subject: [PATCH 23/36] refactor: update AddressType.swift --- IVPNClient/Enums/AddressType.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IVPNClient/Enums/AddressType.swift b/IVPNClient/Enums/AddressType.swift index 2ba6936bd..50acadd65 100644 --- a/IVPNClient/Enums/AddressType.swift +++ b/IVPNClient/Enums/AddressType.swift @@ -30,9 +30,9 @@ enum AddressType { case other static func validateIpAddress(_ address: String) -> AddressType { - if let _ = IPv4Address(address) { + if IPv4Address(address) != nil { return .IPv4 - } else if let _ = IPv6Address(address) { + } else if IPv6Address(address) != nil { return .IPv6 } else { return .other From 8691e57ba04db2881d9908ae345d627b8dcb02e1 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 1 Feb 2024 12:17:51 +0100 Subject: [PATCH 24/36] refactor: update AppDelegate.swift --- IVPNClient/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IVPNClient/AppDelegate.swift b/IVPNClient/AppDelegate.swift index 5bd838243..7da306a0a 100644 --- a/IVPNClient/AppDelegate.swift +++ b/IVPNClient/AppDelegate.swift @@ -364,7 +364,7 @@ extension AppDelegate: UIApplicationDelegate { } } - func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { + func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { let endpoint = url.lastPathComponent handleURLEndpoint(endpoint) return true From 17ef976283958e053e40129b00671b2e16ee1bab Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 1 Feb 2024 12:20:09 +0100 Subject: [PATCH 25/36] refactor: update IVPNWidget.swift --- IVPNWidget/IVPNWidget.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/IVPNWidget/IVPNWidget.swift b/IVPNWidget/IVPNWidget.swift index dcb8dae29..389e9bdae 100644 --- a/IVPNWidget/IVPNWidget.swift +++ b/IVPNWidget/IVPNWidget.swift @@ -30,16 +30,16 @@ struct Provider: TimelineProvider { SimpleEntry(date: Date()) } - func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { + func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) { let entry = SimpleEntry(date: Date()) completion(entry) } - func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { + func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { geoLookup(completion: completion) } - func geoLookup(completion: @escaping (Timeline) -> ()) { + func geoLookup(completion: @escaping (Timeline) -> Void) { var entries: [SimpleEntry] = [] // Generate a timeline consisting of five entries an hour apart, starting from the current date. @@ -70,7 +70,6 @@ struct Provider: TimelineProvider { completion(timeline) case .failure: completion(timeline) - break } } } @@ -80,7 +79,7 @@ struct SimpleEntry: TimelineEntry { let date: Date } -struct EntryView : View { +struct EntryView: View { var entry: Provider.Entry var body: some View { From 978f203927252d495630cb1259eb9c0feb54352b Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 1 Feb 2024 12:23:38 +0100 Subject: [PATCH 26/36] refactor: update V2RayCore.swift --- IVPNClient/Models/V2Ray/V2RayCore.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IVPNClient/Models/V2Ray/V2RayCore.swift b/IVPNClient/Models/V2Ray/V2RayCore.swift index 2b2fdda7a..dd439a374 100644 --- a/IVPNClient/Models/V2Ray/V2RayCore.swift +++ b/IVPNClient/Models/V2Ray/V2RayCore.swift @@ -36,7 +36,7 @@ class V2RayCore { func start() -> Error? { let _ = close() - var error: Error? = nil + var error: Error? guard let config = makeConfig() else { return NSError(domain: "", code: 99, userInfo: [NSLocalizedDescriptionKey: "V2Ray configuration cannot be loaded"]) @@ -52,7 +52,7 @@ class V2RayCore { } func close() -> Error? { - var error: Error? = nil + var error: Error? if let instance = instance { var stopError: NSError? From 4d94306970995b142ec01cfd289a2950797ac19b Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 1 Feb 2024 12:25:21 +0100 Subject: [PATCH 27/36] refactor: update V2RayConfig.swift --- IVPNClient/Models/V2Ray/V2RayConfig.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IVPNClient/Models/V2Ray/V2RayConfig.swift b/IVPNClient/Models/V2Ray/V2RayConfig.swift index 217a1a788..a6e241203 100644 --- a/IVPNClient/Models/V2Ray/V2RayConfig.swift +++ b/IVPNClient/Models/V2Ray/V2RayConfig.swift @@ -112,7 +112,7 @@ struct V2RayConfig: Codable { let connection: [String] let pragma: String - enum CodingKeys : String, CodingKey { + enum CodingKeys: String, CodingKey { case host = "Host" case userAgent = "User-Agent" case acceptEncoding = "Accept-Encoding" From f56a6ca0eaaeeb44b2b91a8e751b60feb59f32d9 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Fri, 2 Feb 2024 12:48:20 +0100 Subject: [PATCH 28/36] feat(account): update AccountViewController --- IVPNClient/Scenes/AccountScreen/AccountViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IVPNClient/Scenes/AccountScreen/AccountViewController.swift b/IVPNClient/Scenes/AccountScreen/AccountViewController.swift index 507f5ead5..36072ab40 100644 --- a/IVPNClient/Scenes/AccountScreen/AccountViewController.swift +++ b/IVPNClient/Scenes/AccountScreen/AccountViewController.swift @@ -180,7 +180,7 @@ extension AccountViewController { logOut(deleteSession: false, deleteSettings: deleteSettings) navigationController?.dismiss(animated: true) } else { - showActionAlert(title: "Error with removing session", message: "Unable to contact server to log out. Please check Internet connectivity. Do you want to force log out? This device will continue to count towards your device limit.", action: "Force log out", cancelHandler: { _ in + showActionAlert(title: "Unable to contact server to log out", message: "Please check Internet connectivity. Do you want to force log out? This device will continue to count towards your device limit.", action: "Force log out", cancelHandler: { _ in NotificationCenter.default.post(name: Notification.Name.UpdateGeoLocation, object: nil) }, actionHandler: { [self] _ in forceLogOut = true From 31d2f45df026f078d4c93d20d3a1979e3520f593 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Sat, 3 Feb 2024 17:45:56 +0100 Subject: [PATCH 29/36] fix(map): update MapScrollView.swift --- IVPNClient/Scenes/MainScreen/Map/MapScrollView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IVPNClient/Scenes/MainScreen/Map/MapScrollView.swift b/IVPNClient/Scenes/MainScreen/Map/MapScrollView.swift index fa8253147..24c190c54 100644 --- a/IVPNClient/Scenes/MainScreen/Map/MapScrollView.swift +++ b/IVPNClient/Scenes/MainScreen/Map/MapScrollView.swift @@ -295,7 +295,7 @@ class MapScrollView: UIScrollView { marker.frame = CGRect(x: 55 - 3, y: 18, width: 6, height: 6) } - if city == "New Jersey, NJ" { + if city == "Secaucus, NJ" { button.titleEdgeInsets = UIEdgeInsets(top: 22, left: 93, bottom: 0, right: 0) button.frame = CGRect(x: point.0 - 85, y: point.1 - 21, width: 170, height: 20) marker.frame = CGRect(x: 85 - 3, y: 18, width: 6, height: 6) From b7ef499a5d880e1ec8cc3d0cc5d6c5a18578286e Mon Sep 17 00:00:00 2001 From: Gaurav K <1673315+gauravkeshre@users.noreply.github.com> Date: Tue, 6 Feb 2024 00:37:27 +0530 Subject: [PATCH 30/36] Update CONTRIBUTING.md --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index c305cde2c..c91120d26 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -59,7 +59,7 @@ In case when there is no issue: / ``` -Where can be `epic`, `feature`, `task`, `bugfix`, `hotfix` or `release`. +Where `` can be `epic`, `feature`, `task`, `bugfix`, `hotfix` or `release`. ### Branches From f70ffaadeb32de15c1cd73b9e58686792e959c5a Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Mon, 12 Feb 2024 13:21:29 +0100 Subject: [PATCH 31/36] docs: update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fd220405..e7e04c279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## 2.12.0 - 2024-02-12 + +[NEW] Device Management +[FIXED] Selecting individual servers with IKEv2 +[FIXED] Minor UI issues on the server map + ## 2.11.1 - 2023-12-05 [NEW] Option to Block LAN traffic when connected to an untrusted network From fec52af1f86bff1082f09d7ca0bade356a4a1b4e Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Fri, 23 Feb 2024 11:08:00 +0100 Subject: [PATCH 32/36] fix: update ServiceStatus.swift --- IVPNClient/Models/ServiceStatus.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IVPNClient/Models/ServiceStatus.swift b/IVPNClient/Models/ServiceStatus.swift index 34ab40996..2397181d5 100644 --- a/IVPNClient/Models/ServiceStatus.swift +++ b/IVPNClient/Models/ServiceStatus.swift @@ -91,7 +91,7 @@ struct ServiceStatus: Codable { } func isNewStyleAccount() -> Bool { - guard let username = username else { + guard let username = KeyChain.username else { return true } From 337f42db795763278a0c57e8bb71587d492276d9 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Fri, 23 Feb 2024 13:39:09 +0100 Subject: [PATCH 33/36] fix: update ServiceStatus.swift --- IVPNClient/Models/ServiceStatus.swift | 2 +- IVPNClient/ViewModels/AccountViewModel.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/IVPNClient/Models/ServiceStatus.swift b/IVPNClient/Models/ServiceStatus.swift index 2397181d5..5015caadc 100644 --- a/IVPNClient/Models/ServiceStatus.swift +++ b/IVPNClient/Models/ServiceStatus.swift @@ -35,7 +35,7 @@ struct ServiceStatus: Codable { let upgradeToUrl: String? let paymentMethod: String? let capabilities: [String]? - let deviceManagement: Bool + let deviceManagement: Bool? // MARK: - Initialize - diff --git a/IVPNClient/ViewModels/AccountViewModel.swift b/IVPNClient/ViewModels/AccountViewModel.swift index 68a65ac44..62bb4afe3 100644 --- a/IVPNClient/ViewModels/AccountViewModel.swift +++ b/IVPNClient/ViewModels/AccountViewModel.swift @@ -51,7 +51,7 @@ struct AccountViewModel { } var deviceManagement: Bool { - return serviceStatus.deviceManagement + return serviceStatus.deviceManagement ?? false } var deviceName: String? { From e41c6a44ddfa4c845c329f68ab7f695da0c7d583 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Fri, 23 Feb 2024 13:39:27 +0100 Subject: [PATCH 34/36] chore: update app version and build number --- IVPNClient.xcodeproj/project.pbxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/IVPNClient.xcodeproj/project.pbxproj b/IVPNClient.xcodeproj/project.pbxproj index 386cb68ec..5cd4e16c3 100644 --- a/IVPNClient.xcodeproj/project.pbxproj +++ b/IVPNClient.xcodeproj/project.pbxproj @@ -2808,7 +2808,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = IVPNClient/IVPNClient.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 10; DEVELOPMENT_TEAM = WQXXM75BYN; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -2826,7 +2826,7 @@ "$(inherited)", "$(PROJECT_DIR)/IVPNClient/liboqs", ); - MARKETING_VERSION = 2.7.0; + MARKETING_VERSION = 2.12.1; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "-D DEBUG"; @@ -3297,7 +3297,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = IVPNClient/IVPNClient.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 10; DEVELOPMENT_TEAM = WQXXM75BYN; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -3315,7 +3315,7 @@ "$(inherited)", "$(PROJECT_DIR)/IVPNClient/liboqs", ); - MARKETING_VERSION = 2.7.0; + MARKETING_VERSION = 2.12.1; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "-D DEBUG"; @@ -3336,7 +3336,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = IVPNClient/IVPNClient.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 10; DEVELOPMENT_TEAM = WQXXM75BYN; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -3354,7 +3354,7 @@ "$(inherited)", "$(PROJECT_DIR)/IVPNClient/liboqs", ); - MARKETING_VERSION = 2.7.0; + MARKETING_VERSION = 2.12.1; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "-D RELEASE"; From 475b2b6900517a130e6d51a9d1cc25e31ccf3cea Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Sat, 24 Feb 2024 08:25:20 +0100 Subject: [PATCH 35/36] docs: update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7e04c279..dd145896f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## 2.12.1 - 2024-02-24 + +[FIXED] In-app payments for legacy accounts + ## 2.12.0 - 2024-02-12 [NEW] Device Management From 94ab3005f6613d0dbd5b3912f7297d5a24e606e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 22:46:27 +0000 Subject: [PATCH 36/36] build(deps): bump google.golang.org/protobuf in /V2RayControl Bumps google.golang.org/protobuf from 1.30.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] --- V2RayControl/go.mod | 2 +- V2RayControl/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/V2RayControl/go.mod b/V2RayControl/go.mod index 97dc39a93..da4a8bdca 100644 --- a/V2RayControl/go.mod +++ b/V2RayControl/go.mod @@ -41,7 +41,7 @@ require ( golang.org/x/tools v0.13.0 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.56.3 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/V2RayControl/go.sum b/V2RayControl/go.sum index 67f9271b9..3d9082a5e 100644 --- a/V2RayControl/go.sum +++ b/V2RayControl/go.sum @@ -453,8 +453,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=