diff --git a/DuckDuckGo/NetworkProtectionDebugViewController.swift b/DuckDuckGo/NetworkProtectionDebugViewController.swift index 73f2453a99..0b8a4d5760 100644 --- a/DuckDuckGo/NetworkProtectionDebugViewController.swift +++ b/DuckDuckGo/NetworkProtectionDebugViewController.swift @@ -37,19 +37,20 @@ import NetworkProtection // swiftlint:disable:next type_body_length final class NetworkProtectionDebugViewController: UITableViewController { private let titles = [ + Sections.featureVisibility: "Feature Visibility", Sections.clearData: "Clear Data", Sections.debugFeature: "Debug Features", Sections.debugCommand: "Debug Commands", Sections.simulateFailure: "Simulate Failure", Sections.registrationKey: "Registration Key", Sections.networkPath: "Network Path", - Sections.lastDisconnectError: "Last Disconnect Error", Sections.connectionTest: "Connection Test", - Sections.vpnConfiguration: "VPN Configuration" - + Sections.vpnConfiguration: "VPN Configuration", + Sections.vpnMetadata: "VPN Metadata", ] enum Sections: Int, CaseIterable { + case featureVisibility case clearData case debugFeature case debugCommand @@ -57,15 +58,17 @@ final class NetworkProtectionDebugViewController: UITableViewController { case registrationKey case connectionTest case networkPath - case lastDisconnectError case vpnConfiguration + case vpnMetadata } - enum ClearDataRows: Int, CaseIterable { + enum FeatureVisibilityRows: Int, CaseIterable { + case toggleSelectedEnvironment + } + enum ClearDataRows: Int, CaseIterable { case clearAuthToken case clearAllVPNData - } enum DebugFeatureRows: Int, CaseIterable { @@ -93,10 +96,6 @@ final class NetworkProtectionDebugViewController: UITableViewController { case networkPath } - enum LastDisconnectErrorRows: Int, CaseIterable { - case lastDisconnectError - } - enum ConnectionTestRows: Int, CaseIterable { case runConnectionTest } @@ -106,6 +105,11 @@ final class NetworkProtectionDebugViewController: UITableViewController { case fullProtocolConfigurationData } + enum MetadataRows: Int, CaseIterable { + case refreshMetadata + case metadataContents + } + // MARK: Properties private let debugFeatures: NetworkProtectionDebugFeatures @@ -113,9 +117,9 @@ final class NetworkProtectionDebugViewController: UITableViewController { private let pathMonitor = NWPathMonitor() private var currentNetworkPath: String? - private var lastDisconnectError: String? private var baseConfigurationData: String? private var fullProtocolConfigurationData: String? + private var vpnMetadata: VPNMetadata? private struct ConnectionTestResult { let interface: String @@ -146,9 +150,12 @@ final class NetworkProtectionDebugViewController: UITableViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - loadLastDisconnectError() loadConfigurationData() startPathMonitor() + + Task { + await self.refreshMetadata() + } } // MARK: Table View @@ -197,22 +204,26 @@ final class NetworkProtectionDebugViewController: UITableViewController { case .networkPath: configure(cell, forNetworkPathRow: indexPath.row) - case .lastDisconnectError: - configure(cell, forLastDisconnectErrorRow: indexPath.row) - case .connectionTest: configure(cell, forConnectionTestRow: indexPath.row) case .vpnConfiguration: configure(cell, forConfigurationRow: indexPath.row) - case.none: + case .vpnMetadata: + configure(cell, forMetadataRow: indexPath.row) + + case .featureVisibility: + configure(cell, forVisibilityRow: indexPath.row) + + case .none: break } return cell } + // swiftlint:disable:next cyclomatic_complexity override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch Sections(rawValue: section) { case .clearData: return ClearDataRows.allCases.count @@ -221,9 +232,10 @@ final class NetworkProtectionDebugViewController: UITableViewController { case .simulateFailure: return SimulateFailureRows.allCases.count case .registrationKey: return RegistrationKeyRows.allCases.count case .networkPath: return NetworkPathRows.allCases.count - case .lastDisconnectError: return LastDisconnectErrorRows.allCases.count case .connectionTest: return ConnectionTestRows.allCases.count + connectionTestResults.count case .vpnConfiguration: return ConfigurationRows.allCases.count + case .vpnMetadata: return MetadataRows.allCases.count + case .featureVisibility: return FeatureVisibilityRows.allCases.count case .none: return 0 } @@ -248,8 +260,6 @@ final class NetworkProtectionDebugViewController: UITableViewController { didSelectRegistrationKeyAction(at: indexPath) case .networkPath: break - case .lastDisconnectError: - break case .connectionTest: if indexPath.row == connectionTestResults.count { Task { @@ -258,6 +268,10 @@ final class NetworkProtectionDebugViewController: UITableViewController { } case .vpnConfiguration: break + case .vpnMetadata: + didSelectVPNMetadataAction(at: indexPath) + case .featureVisibility: + didSelectFeatureVisibility(at: indexPath) case .none: break } @@ -415,20 +429,6 @@ final class NetworkProtectionDebugViewController: UITableViewController { pathMonitor.start(queue: .main) } - // MARK: Last disconnect error - - private func configure(_ cell: UITableViewCell, forLastDisconnectErrorRow row: Int) { - cell.textLabel?.font = .monospacedSystemFont(ofSize: 13.0, weight: .regular) - cell.textLabel?.text = lastDisconnectError ?? "Loading Last Disconnect Error..." - } - - private func loadLastDisconnectError() { - Task { @MainActor in - lastDisconnectError = await DefaultVPNMetadataCollector().lastDisconnectError() - tableView.reloadData() - } - } - // MARK: Connection Test private func configure(_ cell: UITableViewCell, forConnectionTestRow row: Int) { @@ -538,7 +538,6 @@ final class NetworkProtectionDebugViewController: UITableViewController { case .none: assertionFailure("Couldn't map configuration row") } - } private func loadConfigurationData() { @@ -576,6 +575,74 @@ final class NetworkProtectionDebugViewController: UITableViewController { } } + // MARK: - VPN Metadata + + private func configure(_ cell: UITableViewCell, forMetadataRow row: Int) { + cell.textLabel?.font = .systemFont(ofSize: 17) + + switch MetadataRows(rawValue: row) { + case .refreshMetadata: + cell.textLabel?.text = "Refresh Metadata" + case .metadataContents: + cell.textLabel?.font = .monospacedSystemFont(ofSize: 13.0, weight: .regular) + cell.textLabel?.text = vpnMetadata?.toPrettyPrintedJSON() ?? "No Metadata" + case .none: + assertionFailure("Couldn't map configuration row") + } + } + + private func didSelectVPNMetadataAction(at indexPath: IndexPath) { + switch MetadataRows(rawValue: indexPath.row) { + case .refreshMetadata: + Task { + await refreshMetadata() + } + case .metadataContents: + break + case .none: + break + } + } + + // MARK: Feature Visibility + + private func configure(_ cell: UITableViewCell, forVisibilityRow row: Int) { + switch FeatureVisibilityRows(rawValue: row) { + case .toggleSelectedEnvironment: + let settings = VPNSettings(defaults: .networkProtectionGroupDefaults) + if settings.selectedEnvironment == .production { + cell.textLabel?.text = "Selected Environment: PRODUCTION" + } else { + cell.textLabel?.text = "Selected Environment: STAGING" + } + case .none: + break + } + } + + @MainActor + private func refreshMetadata() async { + let collector = DefaultVPNMetadataCollector() + self.vpnMetadata = await collector.collectMetadata() + self.tableView.reloadData() + } + + private func didSelectFeatureVisibility(at indexPath: IndexPath) { + switch FeatureVisibilityRows(rawValue: indexPath.row) { + case .toggleSelectedEnvironment: + let vpnSettings = VPNSettings(defaults: .networkProtectionGroupDefaults) + if vpnSettings.selectedEnvironment == .production { + vpnSettings.selectedEnvironment = .staging + } else { + vpnSettings.selectedEnvironment = .production + } + vpnSettings.selectedServer = .automatic + tableView.reloadData() + case .none: + break + } + } + // MARK: Selection Actions private func clearAuthToken() { diff --git a/DuckDuckGo/NetworkProtectionTunnelController.swift b/DuckDuckGo/NetworkProtectionTunnelController.swift index 77a09a12e1..a2b0f6a06c 100644 --- a/DuckDuckGo/NetworkProtectionTunnelController.swift +++ b/DuckDuckGo/NetworkProtectionTunnelController.swift @@ -136,6 +136,8 @@ final class NetworkProtectionTunnelController: TunnelController { options["activationAttemptId"] = UUID().uuidString as NSString options["authToken"] = try tokenStore.fetchToken() as NSString? + options[NetworkProtectionOptionKey.selectedEnvironment] = VPNSettings(defaults: .networkProtectionGroupDefaults) + .selectedEnvironment.rawValue as NSString do { try tunnelManager.connection.startVPNTunnel(options: options)