diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 8f382e0c301f..b09c0452aa42 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -464,6 +464,8 @@ 7A1A26472A29CF0800B978AA /* RelayFilterDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A26462A29CF0800B978AA /* RelayFilterDataSource.swift */; }; 7A1A26492A29D48A00B978AA /* RelayFilterCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A26482A29D48A00B978AA /* RelayFilterCellFactory.swift */; }; 7A21DACF2A30AA3700A787A9 /* UITextField+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */; }; + 7A27E3C92CAE85710088BCFF /* SettingsInfoButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27E3C82CAE85660088BCFF /* SettingsInfoButtonItem.swift */; }; + 7A27E3CB2CAE861D0088BCFF /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27E3CA2CAE86170088BCFF /* SettingsViewModel.swift */; }; 7A28826A2BA8336600FD9F20 /* VPNSettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2882692BA8336600FD9F20 /* VPNSettingsCoordinator.swift */; }; 7A2960F62A963F7500389B82 /* AlertCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2960F52A963F7500389B82 /* AlertCoordinator.swift */; }; 7A2960FD2A964BB700389B82 /* AlertPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2960FC2A964BB700389B82 /* AlertPresentation.swift */; }; @@ -571,10 +573,10 @@ 7A9CCCC22A96302800DD6A34 /* SafariCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9CCCB02A96302800DD6A34 /* SafariCoordinator.swift */; }; 7A9CCCC32A96302800DD6A34 /* ApplicationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9CCCB12A96302800DD6A34 /* ApplicationCoordinator.swift */; }; 7A9CCCC42A96302800DD6A34 /* TunnelCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9CCCB22A96302800DD6A34 /* TunnelCoordinator.swift */; }; + 7A9F28FC2CA69D0C005F2089 /* DAITASettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F28FB2CA69D04005F2089 /* DAITASettingsTests.swift */; }; 7A9F29392CABFAFC005F2089 /* InfoHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F29382CABFAEC005F2089 /* InfoHeaderView.swift */; }; 7A9F293B2CAC4443005F2089 /* InfoHeaderConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F293A2CAC4420005F2089 /* InfoHeaderConfig.swift */; }; 7A9F293D2CAD2FD5005F2089 /* InfoModalConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F293C2CAD2FCF005F2089 /* InfoModalConfig.swift */; }; - 7A9F28FC2CA69D0C005F2089 /* DAITASettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F28FB2CA69D04005F2089 /* DAITASettingsTests.swift */; }; 7A9FA1422A2E3306000B728D /* CheckboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FA1412A2E3306000B728D /* CheckboxView.swift */; }; 7A9FA1442A2E3FE5000B728D /* CheckableSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FA1432A2E3FE5000B728D /* CheckableSettingsCell.swift */; }; 7AA513862BC91C6B00D081A4 /* LogRotationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA513852BC91C6B00D081A4 /* LogRotationTests.swift */; }; @@ -866,7 +868,7 @@ F03580252A13842C00E5DAFD /* IncreasedHitButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03580242A13842C00E5DAFD /* IncreasedHitButton.swift */; }; F03A69F72C2AD2D6000E2E7E /* TimeInterval+Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03A69F62C2AD2D5000E2E7E /* TimeInterval+Timeout.swift */; }; F03A69F92C2AD414000E2E7E /* FormsheetPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03A69F82C2AD413000E2E7E /* FormsheetPresentationController.swift */; }; - F041BE4F2C983C2B0083EC28 /* VPNSettingsPromptAlertItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F041BE4E2C983C2B0083EC28 /* VPNSettingsPromptAlertItem.swift */; }; + F041BE4F2C983C2B0083EC28 /* DAITASettingsPromptItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F041BE4E2C983C2B0083EC28 /* DAITASettingsPromptItem.swift */; }; F041BE532C9878B60083EC28 /* ConnectionConfigurationBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F041BE522C9878B60083EC28 /* ConnectionConfigurationBuilder.swift */; }; F04413612BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */; }; F04413622BA45CE30018A6EE /* CustomListLocationNodeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */; }; @@ -1797,6 +1799,8 @@ 7A1A26482A29D48A00B978AA /* RelayFilterCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilterCellFactory.swift; sourceTree = ""; }; 7A1A264A2A29D65E00B978AA /* SelectableSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableSettingsCell.swift; sourceTree = ""; }; 7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Appearance.swift"; sourceTree = ""; }; + 7A27E3C82CAE85660088BCFF /* SettingsInfoButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsInfoButtonItem.swift; sourceTree = ""; }; + 7A27E3CA2CAE86170088BCFF /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = ""; }; 7A2882692BA8336600FD9F20 /* VPNSettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNSettingsCoordinator.swift; sourceTree = ""; }; 7A2960F52A963F7500389B82 /* AlertCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertCoordinator.swift; sourceTree = ""; }; 7A2960FC2A964BB700389B82 /* AlertPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertPresentation.swift; sourceTree = ""; }; @@ -1890,10 +1894,10 @@ 7A9CCCB02A96302800DD6A34 /* SafariCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafariCoordinator.swift; sourceTree = ""; }; 7A9CCCB12A96302800DD6A34 /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplicationCoordinator.swift; sourceTree = ""; }; 7A9CCCB22A96302800DD6A34 /* TunnelCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelCoordinator.swift; sourceTree = ""; }; + 7A9F28FB2CA69D04005F2089 /* DAITASettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DAITASettingsTests.swift; sourceTree = ""; }; 7A9F29382CABFAEC005F2089 /* InfoHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoHeaderView.swift; sourceTree = ""; }; 7A9F293A2CAC4420005F2089 /* InfoHeaderConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoHeaderConfig.swift; sourceTree = ""; }; 7A9F293C2CAD2FCF005F2089 /* InfoModalConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoModalConfig.swift; sourceTree = ""; }; - 7A9F28FB2CA69D04005F2089 /* DAITASettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DAITASettingsTests.swift; sourceTree = ""; }; 7A9FA1412A2E3306000B728D /* CheckboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxView.swift; sourceTree = ""; }; 7A9FA1432A2E3FE5000B728D /* CheckableSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckableSettingsCell.swift; sourceTree = ""; }; 7AA513852BC91C6B00D081A4 /* LogRotationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogRotationTests.swift; sourceTree = ""; }; @@ -2087,7 +2091,7 @@ F03580242A13842C00E5DAFD /* IncreasedHitButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncreasedHitButton.swift; sourceTree = ""; }; F03A69F62C2AD2D5000E2E7E /* TimeInterval+Timeout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TimeInterval+Timeout.swift"; sourceTree = ""; }; F03A69F82C2AD413000E2E7E /* FormsheetPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormsheetPresentationController.swift; sourceTree = ""; }; - F041BE4E2C983C2B0083EC28 /* VPNSettingsPromptAlertItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNSettingsPromptAlertItem.swift; sourceTree = ""; }; + F041BE4E2C983C2B0083EC28 /* DAITASettingsPromptItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DAITASettingsPromptItem.swift; sourceTree = ""; }; F041BE522C9878B60083EC28 /* ConnectionConfigurationBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionConfigurationBuilder.swift; sourceTree = ""; }; F04413602BA45CD70018A6EE /* CustomListLocationNodeBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomListLocationNodeBuilder.swift; sourceTree = ""; }; F04AF92C2C466013004A8314 /* EphemeralPeerNegotiationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EphemeralPeerNegotiationState.swift; sourceTree = ""; }; @@ -2793,6 +2797,7 @@ isa = PBXGroup; children = ( 7A9FA1432A2E3FE5000B728D /* CheckableSettingsCell.swift */, + F041BE4E2C983C2B0083EC28 /* DAITASettingsPromptItem.swift */, 7A1A264A2A29D65E00B978AA /* SelectableSettingsCell.swift */, 5819C2162729595500D6EC38 /* SettingsAddDNSEntryCell.swift */, 582BB1AE229566420055B6EF /* SettingsCell.swift */, @@ -2802,11 +2807,13 @@ 7A83C4012A57FAA800DFB83A /* SettingsDNSInfoCell.swift */, 584D26C5270C8741004EA533 /* SettingsDNSTextCell.swift */, 7AC8A3AD2ABC6FBB00DC4939 /* SettingsHeaderView.swift */, + 7A27E3C82CAE85660088BCFF /* SettingsInfoButtonItem.swift */, 7A42DEC82A05164100B209BE /* SettingsInputCell.swift */, 58677711290976FB006F721F /* SettingsInteractor.swift */, 5867770F290975E8006F721F /* SettingsInteractorFactory.swift */, 58ACF64A26553C3F00ACE4B7 /* SettingsSwitchCell.swift */, 58CCA01122424D11004F3011 /* SettingsViewController.swift */, + 7A27E3CA2CAE86170088BCFF /* SettingsViewModel.swift */, ); path = Settings; sourceTree = ""; @@ -2835,7 +2842,6 @@ 587EB6732714520600123C75 /* VPNSettingsDataSourceDelegate.swift */, 7A6F2FAE2AFE36E7006D0856 /* VPNSettingsInfoButtonItem.swift */, 5871167E2910035700D41AAC /* VPNSettingsInteractor.swift */, - F041BE4E2C983C2B0083EC28 /* VPNSettingsPromptAlertItem.swift */, 58ACF6482655365700ACE4B7 /* VPNSettingsViewController.swift */, 587EB671271451E300123C75 /* VPNSettingsViewModel.swift */, ); @@ -5606,7 +5612,7 @@ 7AC8A3AF2ABC71D600DC4939 /* TermsOfServiceCoordinator.swift in Sources */, 58FF9FE22B075BA600E4C97D /* EditAccessMethodSectionIdentifier.swift in Sources */, F0C2AEFD2A0BB5CC00986207 /* NotificationProviderIdentifier.swift in Sources */, - F041BE4F2C983C2B0083EC28 /* VPNSettingsPromptAlertItem.swift in Sources */, + F041BE4F2C983C2B0083EC28 /* DAITASettingsPromptItem.swift in Sources */, 7A58699B2B482FE200640D27 /* UITableViewCell+Disable.swift in Sources */, 7AB2B6702BA1EB8C00B03E3B /* ListCustomListViewController.swift in Sources */, 7A9CCCB72A96302800DD6A34 /* RevokedCoordinator.swift in Sources */, @@ -5644,6 +5650,7 @@ A91614D62B10B26B00F416EB /* TunnelControlViewModel.swift in Sources */, 7A5869972B32EA4500640D27 /* AppButton.swift in Sources */, 586C0D8F2B03D88100E7CDD7 /* ProxyProtocolConfigurationItemIdentifier.swift in Sources */, + 7A27E3CB2CAE861D0088BCFF /* SettingsViewModel.swift in Sources */, 588527B2276B3F0700BAA373 /* LoadTunnelConfigurationOperation.swift in Sources */, 7A9F29392CABFAFC005F2089 /* InfoHeaderView.swift in Sources */, 58DFF7D22B0256A300F864E0 /* MarkdownStylingOptions.swift in Sources */, @@ -5880,6 +5887,7 @@ 58293FAE2510CA58005D0BB5 /* ProblemReportViewController.swift in Sources */, 58B9EB152489139B00095626 /* RESTError+Display.swift in Sources */, 587B753F2668E5A700DEF7E9 /* NotificationContainerView.swift in Sources */, + 7A27E3C92CAE85710088BCFF /* SettingsInfoButtonItem.swift in Sources */, 58F2E144276A13F300A79513 /* StartTunnelOperation.swift in Sources */, 58CCA01E2242787B004F3011 /* AccountTextField.swift in Sources */, 586E54FB27A2DF6D0029B88B /* SendTunnelProviderMessageOperation.swift in Sources */, diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift index 95fc4388054a..6cfbce70a131 100644 --- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift +++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift @@ -193,6 +193,7 @@ public enum AccessibilityIdentifier: String { // DAITA case daitaSwitch case daitaPromptAlert + case daitaDirectOnlySwitch // Quantum resistance case quantumResistanceAutomatic diff --git a/ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift b/ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift index c9a44223ff17..2e9501f25a36 100644 --- a/ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift @@ -238,7 +238,8 @@ final class SettingsCoordinator: Coordinator, Presentable, Presenting, SettingsV switch route { case .root: let controller = SettingsViewController( - interactor: interactorFactory.makeSettingsInteractor() + interactor: interactorFactory.makeSettingsInteractor(), + alertPresenter: AlertPresenter(context: self) ) controller.delegate = self return .viewController(controller) diff --git a/ios/MullvadVPN/Extensions/UIListContentConfiguration+Extensions.swift b/ios/MullvadVPN/Extensions/UIListContentConfiguration+Extensions.swift index 3eefba09fb2d..385e9a1f6caa 100644 --- a/ios/MullvadVPN/Extensions/UIListContentConfiguration+Extensions.swift +++ b/ios/MullvadVPN/Extensions/UIListContentConfiguration+Extensions.swift @@ -14,7 +14,6 @@ extension UIListContentConfiguration { var configuration = cell() configuration.textProperties.font = .systemFont(ofSize: 17) configuration.textProperties.color = .Cell.titleTextColor.withAlphaComponent(isEnabled ? 1 : 0.8) - configuration.axesPreservingSuperviewLayoutMargins = .vertical applyMargins(to: &configuration, tableStyle: tableStyle) diff --git a/ios/MullvadVPN/UI appearance/UIMetrics.swift b/ios/MullvadVPN/UI appearance/UIMetrics.swift index aa6502d1263a..9e4997da1ea4 100644 --- a/ios/MullvadVPN/UI appearance/UIMetrics.swift +++ b/ios/MullvadVPN/UI appearance/UIMetrics.swift @@ -81,7 +81,7 @@ enum UIMetrics { /// Cell layout margins used in table views that use inset style. static let insetLayoutMargins = NSDirectionalEdgeInsets(top: 16, leading: 24, bottom: 16, trailing: 24) - static let apiAccessLayoutMargins = NSDirectionalEdgeInsets(top: 20, leading: 16, bottom: 20, trailing: 16) + static let apiAccessLayoutMargins = NSDirectionalEdgeInsets(top: 8, leading: 24, bottom: 24, trailing: 24) static let apiAccessInsetLayoutMargins = NSDirectionalEdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16) static let settingsValidationErrorLayoutMargins = NSDirectionalEdgeInsets( top: 8, diff --git a/ios/MullvadVPN/View controllers/Settings/DAITASettingsPromptItem.swift b/ios/MullvadVPN/View controllers/Settings/DAITASettingsPromptItem.swift new file mode 100644 index 000000000000..c67578027c73 --- /dev/null +++ b/ios/MullvadVPN/View controllers/Settings/DAITASettingsPromptItem.swift @@ -0,0 +1,45 @@ +// +// DAITASettingsPromptItem.swift +// MullvadVPN +// +// Created by Mojgan on 2024-09-16. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation +enum DAITASettingsPromptItem: CustomStringConvertible { + case daitaSettingIncompatibleWithSinglehop(Setting) + case daitaSettingIncompatibleWithMultihop(Setting) + + enum Setting { + case daita + case directOnly + } + + var title: String { + switch self { + case let .daitaSettingIncompatibleWithSinglehop(setting), let .daitaSettingIncompatibleWithMultihop(setting): + switch setting { + case .daita: + "DAITA" + case .directOnly: + "direct only" + } + } + } + + var description: String { + switch self { + case .daitaSettingIncompatibleWithSinglehop: + """ + Not all our servers are DAITA-enabled. In order to use the internet, you might have to \ + select a new location after enabling. + """ + case .daitaSettingIncompatibleWithMultihop: + """ + Not all our servers are DAITA-enabled. In order to use the internet, you might have to \ + select a new entry location after enabling. + """ + } + } +} diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsCellFactory.swift b/ios/MullvadVPN/View controllers/Settings/SettingsCellFactory.swift index 1085f7f71c9f..d079501f1f29 100644 --- a/ios/MullvadVPN/View controllers/Settings/SettingsCellFactory.swift +++ b/ios/MullvadVPN/View controllers/Settings/SettingsCellFactory.swift @@ -6,15 +6,26 @@ // Copyright © 2023 Mullvad VPN AB. All rights reserved. // +import MullvadSettings import UIKit -struct SettingsCellFactory: CellFactoryProtocol { +protocol SettingsCellEventHandler { + func showInfo(for button: SettingsInfoButtonItem) + func switchDaitaState(_ settings: DAITASettings) + func switchDaitaDirectOnlyState(_ settings: DAITASettings) +} + +final class SettingsCellFactory: CellFactoryProtocol { let tableView: UITableView + var delegate: SettingsCellEventHandler? + var viewModel: SettingsViewModel private let interactor: SettingsInteractor init(tableView: UITableView, interactor: SettingsInteractor) { self.tableView = tableView self.interactor = interactor + + viewModel = SettingsViewModel(from: interactor.tunnelSettings) } func makeCell(for item: SettingsDataSource.Item, indexPath: IndexPath) -> UITableViewCell { @@ -92,6 +103,59 @@ struct SettingsCellFactory: CellFactoryProtocol { cell.detailTitleLabel.text = nil cell.accessibilityIdentifier = item.accessibilityIdentifier cell.disclosureType = .chevron + + case .daita: + guard let cell = cell as? SettingsSwitchCell else { return } + + cell.titleLabel.text = NSLocalizedString( + "DAITA_LABEL", + tableName: "Settings", + value: "DAITA", + comment: "" + ) + cell.accessibilityIdentifier = item.accessibilityIdentifier + cell.setOn(viewModel.daitaSettings.daitaState.isEnabled, animated: false) + + cell.infoButtonHandler = { [weak self] in + self?.delegate?.showInfo(for: .daita) + } + + cell.action = { [weak self] isEnabled in + guard let self else { return } + + let state: DAITAState = isEnabled ? .on : .off + delegate?.switchDaitaState(DAITASettings( + daitaState: state, + directOnlyState: viewModel.daitaSettings.directOnlyState + )) + } + + case .daitaDirectOnly: + guard let cell = cell as? SettingsSwitchCell else { return } + + cell.titleLabel.text = NSLocalizedString( + "DAITA_DIRECT_ONLY_LABEL", + tableName: "Settings", + value: "Direct only", + comment: "" + ) + cell.accessibilityIdentifier = item.accessibilityIdentifier + cell.setOn(viewModel.daitaSettings.directOnlyState.isEnabled, animated: false) + cell.setSwitchEnabled(viewModel.daitaSettings.daitaState.isEnabled) + + cell.infoButtonHandler = { [weak self] in + self?.delegate?.showInfo(for: .daitaDirectOnly) + } + + cell.action = { [weak self] isEnabled in + guard let self else { return } + + let state: DirectOnlyState = isEnabled ? .on : .off + delegate?.switchDaitaDirectOnlyState(DAITASettings( + daitaState: viewModel.daitaSettings.daitaState, + directOnlyState: state + )) + } } } } diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsDataSource.swift b/ios/MullvadVPN/View controllers/Settings/SettingsDataSource.swift index 87143fc84f34..eb9236bc5826 100644 --- a/ios/MullvadVPN/View controllers/Settings/SettingsDataSource.swift +++ b/ios/MullvadVPN/View controllers/Settings/SettingsDataSource.swift @@ -12,10 +12,16 @@ import UIKit final class SettingsDataSource: UITableViewDiffableDataSource, UITableViewDelegate { enum CellReuseIdentifiers: String, CaseIterable { - case basicCell + case basic + case daita var reusableViewClass: AnyClass { - SettingsCell.self + switch self { + case .basic: + SettingsCell.self + case .daita: + SettingsSwitchCell.self + } } } @@ -28,6 +34,7 @@ final class SettingsDataSource: UITableViewDiffableDataSource Bool { - if case .version = itemIdentifier(for: indexPath) { - return false - } else { - return true + switch itemIdentifier(for: indexPath) { + case .vpnSettings, .problemReport, .faq, .apiAccess: + true + case .version, .daita, .daitaDirectOnly, .none: + false } } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let item = itemIdentifier(for: indexPath) else { return } - - delegate?.settingsDataSource(self, didSelectItem: item) + delegate?.didSelectItem(item: item) } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { @@ -143,9 +163,11 @@ final class SettingsDataSource: UITableViewDiffableDataSource() - snapshot.appendSections([.main]) + snapshot.appendSections([.daita, .main]) if interactor.deviceState.isLoggedIn { + snapshot.appendItems([.daita], toSection: .daita) + snapshot.appendItems([.daitaDirectOnly], toSection: .daita) snapshot.appendItems([.vpnSettings], toSection: .main) } @@ -158,3 +180,72 @@ final class SettingsDataSource: UITableViewDiffableDataSource Void)? = nil, + onDiscard: @escaping () -> Void + ) { + let updateSettings = { [weak self] in + self?.settingsCellFactory.viewModel.setDAITASettings(settings) + self?.interactor.updateDAITASettings(settings) + + onSave?() + } + + var promptItemSetting: DAITASettingsPromptItem.Setting? + switch item { + case .daita: + promptItemSetting = .daita + case .daitaDirectOnly: + promptItemSetting = .directOnly + default: + break + } + + if let promptItemSetting, let error = interactor.evaluateDaitaSettingsCompatibility(settings) { + switch error { + case .singlehop: + delegate?.showPrompt( + for: .daitaSettingIncompatibleWithSinglehop(promptItemSetting), + onSave: { updateSettings() }, + onDiscard: onDiscard + ) + case .multihop: + delegate?.showPrompt( + for: .daitaSettingIncompatibleWithMultihop(promptItemSetting), + onSave: { updateSettings() }, + onDiscard: onDiscard + ) + } + } else { + updateSettings() + } + } +} diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsDataSourceDelegate.swift b/ios/MullvadVPN/View controllers/Settings/SettingsDataSourceDelegate.swift index 30d7e1629a9f..4800271dc8d5 100644 --- a/ios/MullvadVPN/View controllers/Settings/SettingsDataSourceDelegate.swift +++ b/ios/MullvadVPN/View controllers/Settings/SettingsDataSourceDelegate.swift @@ -6,11 +6,15 @@ // Copyright © 2021 Mullvad VPN AB. All rights reserved. // +import MullvadSettings import UIKit protocol SettingsDataSourceDelegate: AnyObject { - func settingsDataSource( - _ dataSource: SettingsDataSource, - didSelectItem item: SettingsDataSource.Item + func didSelectItem(item: SettingsDataSource.Item) + func showInfo(for: SettingsInfoButtonItem) + func showPrompt( + for: DAITASettingsPromptItem, + onSave: @escaping () -> Void, + onDiscard: @escaping () -> Void ) } diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsInfoButtonItem.swift b/ios/MullvadVPN/View controllers/Settings/SettingsInfoButtonItem.swift new file mode 100644 index 000000000000..8fd498928cd0 --- /dev/null +++ b/ios/MullvadVPN/View controllers/Settings/SettingsInfoButtonItem.swift @@ -0,0 +1,46 @@ +// +// SettingsInfoButtonItem.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-10-03. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation + +enum SettingsInfoButtonItem: CustomStringConvertible { + case daita + case daitaDirectOnly + + var description: String { + switch self { + case .daita: + NSLocalizedString( + "DAITA_INFORMATION_TEXT", + tableName: "DAITA", + value: """ + DAITA (Defense against AI-guided Traffic Analysis) hides patterns in your encrypted VPN traffic. \ + If anyone is monitoring your connection, this makes it significantly harder for them to identify \ + what websites you are visiting. + It does this by carefully adding network noise and making all network packets the same size. + Not all our servers are DAITA-enabled. Therefore, we use multihop automatically to enable DAITA \ + with any server. + Attention: Be cautious if you have a limited data plan as this feature will increase your network \ + traffic. This feature can only be used with WireGuard. + """, + comment: "" + ) + case .daitaDirectOnly: + NSLocalizedString( + "DIRECT_ONLY_INFORMATION_TEXT", + tableName: "DAITA", + value: """ + By enabling “Direct only” you will have to manually select a server that is DAITA-enabled. \ + This can cause you to end up in a blocked state until you have selected a compatible server \ + in the “Select location” view. + """, + comment: "" + ) + } + } +} diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsInteractor.swift b/ios/MullvadVPN/View controllers/Settings/SettingsInteractor.swift index c3f07dc72c1d..23ecdc4e9c03 100644 --- a/ios/MullvadVPN/View controllers/Settings/SettingsInteractor.swift +++ b/ios/MullvadVPN/View controllers/Settings/SettingsInteractor.swift @@ -15,13 +15,15 @@ final class SettingsInteractor { var didUpdateDeviceState: ((DeviceState) -> Void)? + var tunnelSettings: LatestTunnelSettings { + tunnelManager.settings + } + var deviceState: DeviceState { tunnelManager.deviceState } - init( - tunnelManager: TunnelManager - ) { + init(tunnelManager: TunnelManager) { self.tunnelManager = tunnelManager let tunnelObserver = @@ -33,4 +35,24 @@ final class SettingsInteractor { self.tunnelObserver = tunnelObserver } + + func updateDAITASettings(_ settings: DAITASettings) { + tunnelManager.updateSettings([.daita(settings)]) + } + + func evaluateDaitaSettingsCompatibility(_ settings: DAITASettings) -> DAITASettingsCompatibilityError? { + guard settings.daitaState.isEnabled else { return nil } + + var tunnelSettings = tunnelSettings + tunnelSettings.daita = settings + + let selectedRelays = try? tunnelManager.selectRelays(tunnelSettings: tunnelSettings) + let multihopEnabled = tunnelSettings.tunnelMultihopState.isEnabled + + return if multihopEnabled { + selectedRelays?.entry == nil ? .multihop : nil + } else { + selectedRelays?.exit == nil ? .singlehop : nil + } + } } diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsSwitchCell.swift b/ios/MullvadVPN/View controllers/Settings/SettingsSwitchCell.swift index e14875601786..83af8f6b0587 100644 --- a/ios/MullvadVPN/View controllers/Settings/SettingsSwitchCell.swift +++ b/ios/MullvadVPN/View controllers/Settings/SettingsSwitchCell.swift @@ -31,7 +31,7 @@ class SettingsSwitchCell: SettingsCell { fatalError("init(coder:) has not been implemented") } - func setEnabled(_ isEnabled: Bool) { + func setSwitchEnabled(_ isEnabled: Bool) { switchContainer.isEnabled = isEnabled } @@ -42,7 +42,7 @@ class SettingsSwitchCell: SettingsCell { override func prepareForReuse() { super.prepareForReuse() - setEnabled(true) + setSwitchEnabled(true) } // MARK: - Actions diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift b/ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift index 27e89a883b1a..01b4df8ea6cb 100644 --- a/ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift +++ b/ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift @@ -7,6 +7,7 @@ // import Foundation +import MullvadSettings import Routing import UIKit @@ -18,17 +19,20 @@ protocol SettingsViewControllerDelegate: AnyObject { ) } -class SettingsViewController: UITableViewController, SettingsDataSourceDelegate { +class SettingsViewController: UITableViewController { weak var delegate: SettingsViewControllerDelegate? private var dataSource: SettingsDataSource? private let interactor: SettingsInteractor + private let alertPresenter: AlertPresenter override var preferredStatusBarStyle: UIStatusBarStyle { .lightContent } - init(interactor: SettingsInteractor) { + init(interactor: SettingsInteractor, alertPresenter: AlertPresenter) { self.interactor = interactor + self.alertPresenter = alertPresenter + super.init(style: .grouped) } @@ -66,16 +70,76 @@ class SettingsViewController: UITableViewController, SettingsDataSourceDelegate dataSource = SettingsDataSource(tableView: tableView, interactor: interactor) dataSource?.delegate = self } +} + +extension SettingsViewController: SettingsDataSourceDelegate { + func didSelectItem(item: SettingsDataSource.Item) { + guard let route = item.navigationRoute else { return } + delegate?.settingsViewController(self, didRequestRoutePresentation: route) + } - // MARK: - SettingsDataSourceDelegate + func showInfo(for item: SettingsInfoButtonItem) { + let presentation = AlertPresentation( + id: "settings-info-alert", + icon: .info, + message: item.description, + buttons: [ + AlertAction( + title: NSLocalizedString( + "SETTINGS_INFO_ALERT_OK_ACTION", + tableName: "Settings", + value: "Got it!", + comment: "" + ), + style: .default + ), + ] + ) + + alertPresenter.showAlert(presentation: presentation, animated: true) + } - func settingsDataSource( - _ dataSource: SettingsDataSource, - didSelectItem item: SettingsDataSource.Item + func showPrompt( + for item: DAITASettingsPromptItem, + onSave: @escaping () -> Void, + onDiscard: @escaping () -> Void ) { - guard let route = item.navigationRoute else { return } + let presentation = AlertPresentation( + id: "settings-daita-prompt", + accessibilityIdentifier: .daitaPromptAlert, + icon: .info, + message: NSLocalizedString( + "SETTINGS_DAITA_ENABLE_TEXT", + tableName: "DAITA", + value: item.description, + comment: "" + ), + buttons: [ + AlertAction( + title: String(format: NSLocalizedString( + "SETTINGS_DAITA_ENABLE_OK_ACTION", + tableName: "DAITA", + value: "Enable %@", + comment: "" + ), item.title), + style: .default, + accessibilityId: .daitaConfirmAlertEnableButton, + handler: { onSave() } + ), + AlertAction( + title: NSLocalizedString( + "SETTINGS_DAITA_ENABLE_CANCEL_ACTION", + tableName: "DAITA", + value: "Back", + comment: "" + ), + style: .default, + handler: { onDiscard() } + ), + ] + ) - delegate?.settingsViewController(self, didRequestRoutePresentation: route) + alertPresenter.showAlert(presentation: presentation, animated: true) } } @@ -84,7 +148,7 @@ extension SettingsDataSource.Item { switch self { case .vpnSettings: return .vpnSettings - case .version: + case .version, .daita, .daitaDirectOnly: return nil case .problemReport: return .problemReport diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsViewModel.swift b/ios/MullvadVPN/View controllers/Settings/SettingsViewModel.swift new file mode 100644 index 000000000000..1e67a4c28500 --- /dev/null +++ b/ios/MullvadVPN/View controllers/Settings/SettingsViewModel.swift @@ -0,0 +1,21 @@ +// +// SettingsViewModel.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-10-03. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import MullvadSettings + +struct SettingsViewModel { + private(set) var daitaSettings: DAITASettings + + mutating func setDAITASettings(_ newSettings: DAITASettings) { + daitaSettings = newSettings + } + + init(from tunnelSettings: LatestTunnelSettings = LatestTunnelSettings()) { + daitaSettings = tunnelSettings.daita + } +} diff --git a/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSCellFactory.swift b/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSCellFactory.swift index 11536c55f31f..639fa2d17b90 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSCellFactory.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSCellFactory.swift @@ -172,7 +172,7 @@ final class CustomDNSCellFactory: CellFactoryProtocol { value: "Use custom DNS server", comment: "" ) - cell.setEnabled(viewModel.customDNSPrecondition == .satisfied) + cell.setSwitchEnabled(viewModel.customDNSPrecondition == .satisfied) cell.setOn(viewModel.effectiveEnableCustomDNS, animated: false) cell.accessibilityHint = viewModel.customDNSPrecondition .localizedDescription(isEditing: isEditing) diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift index 996e340dc4dd..16d6ff24a5cf 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift @@ -15,7 +15,6 @@ protocol VPNSettingsCellEventHandler { func selectCustomPortEntry(_ port: UInt16) -> Bool func selectObfuscationState(_ state: WireGuardObfuscationState) func switchMultihop(_ state: MultihopState) - func switchDaitaState(_ settings: DAITASettings) } final class VPNSettingsCellFactory: CellFactoryProtocol { @@ -206,27 +205,6 @@ final class VPNSettingsCellFactory: CellFactoryProtocol { cell.accessibilityIdentifier = item.accessibilityIdentifier cell.applySubCellStyling() - case .daitaSwitch: - guard let cell = cell as? SettingsSwitchCell else { return } - - cell.titleLabel.text = NSLocalizedString( - "DAITA_LABEL", - tableName: "VPNSettings", - value: "DAITA", - comment: "" - ) - cell.accessibilityIdentifier = item.accessibilityIdentifier - cell.setOn(viewModel.daitaSettings.daitaState.isEnabled, animated: false) - - cell.infoButtonHandler = { [weak self] in - self?.delegate?.showInfo(for: .daita) - } - - cell.action = { [weak self] isEnabled in - let state: DAITAState = isEnabled ? .on : .off - self?.delegate?.switchDaitaState(DAITASettings(daitaState: state)) - } - case .multihopSwitch: guard let cell = cell as? SettingsSwitchCell else { return } diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift index bd24a10f6dda..4113ca8660ca 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift @@ -80,7 +80,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< case quantumResistanceOn case quantumResistanceOff case multihopSwitch - case daitaSwitch static var wireGuardPorts: [Item] { let defaultPorts = VPNSettingsViewModel.defaultWireGuardPorts.map { @@ -125,8 +124,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< return .quantumResistanceOn case .quantumResistanceOff: return .quantumResistanceOff - case .daitaSwitch: - return .daitaSwitch case .multihopSwitch: return .multihopSwitch } @@ -150,8 +147,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< return .quantumResistance case .multihopSwitch: return .multihop - case .daitaSwitch: - return .daita } } } @@ -425,7 +420,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< snapshot.appendItems([.dnsSettings], toSection: .dnsSettings) snapshot.appendItems([.ipOverrides], toSection: .ipOverrides) - snapshot.appendItems([.daitaSwitch, .multihopSwitch], toSection: .privacyAndSecurity) + snapshot.appendItems([.multihopSwitch], toSection: .privacyAndSecurity) applySnapshot(snapshot, animated: animated, completion: completion) } @@ -646,32 +641,6 @@ extension VPNSettingsDataSource: VPNSettingsCellEventHandler { viewModel.setMultihop(state) delegate?.didUpdateTunnelSettings(.multihop(viewModel.multihopState)) } - - func switchDaitaState(_ settings: DAITASettings) { - let updateSettings = { [weak self] in - self?.viewModel.setDAITASettings(settings) - self?.delegate?.didUpdateTunnelSettings(.daita(settings)) - } - - if let error = delegate?.didAttemptToChangeDaitaSettings(settings) { - switch error { - case .singlehop: - delegate?.showPrompt(for: .daitaSettingIncompatibleWithSinglehop) { - updateSettings() - } onDiscard: { [weak self] in - self?.tableView?.reloadData() - } - case .multihop: - delegate?.showPrompt(for: .daitaSettingIncompatibleWithMultihop) { - updateSettings() - } onDiscard: { [weak self] in - self?.tableView?.reloadData() - } - } - } else { - updateSettings() - } - } } // swiftlint:disable:this file_length diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSourceDelegate.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSourceDelegate.swift index 84bf9795d042..de1a602fce27 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSourceDelegate.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSourceDelegate.swift @@ -16,11 +16,9 @@ protocol DNSSettingsDataSourceDelegate: AnyObject { protocol VPNSettingsDataSourceDelegate: AnyObject { func didUpdateTunnelSettings(_ update: TunnelSettingsUpdate) - func didAttemptToChangeDaitaSettings(_ settings: DAITASettings) -> DAITASettingsCompatibilityError? func showInfo(for: VPNSettingsInfoButtonItem) func showDNSSettings() func showIPOverrides() func didSelectWireGuardPort(_ port: UInt16?) - func showPrompt(for: VPNSettingsPromptAlertItem, onSave: @escaping () -> Void, onDiscard: @escaping () -> Void) func humanReadablePortRepresentation() -> String } diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsInfoButtonItem.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsInfoButtonItem.swift index 1ba634301583..27b21c4007e0 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsInfoButtonItem.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsInfoButtonItem.swift @@ -102,7 +102,7 @@ enum VPNSettingsInfoButtonItem: CustomStringConvertible { "DAITA_INFORMATION_TEXT", tableName: "DAITA", value: """ - DAITA (Defence against AI-guided Traffic Analysis) hides patterns in your encrypted VPN traffic. \ + DAITA (Defense against AI-guided Traffic Analysis) hides patterns in your encrypted VPN traffic. \ If anyone is monitoring your connection, this makes it significantly harder for them to identify \ what websites you are visiting. It does this by carefully adding network noise and making all \ network packets the same size. diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsInteractor.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsInteractor.swift index 8857c46493e8..e304a8e4b6b2 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsInteractor.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsInteractor.swift @@ -51,22 +51,6 @@ final class VPNSettingsInteractor { tunnelManager.updateSettings([.relayConstraints(relayConstraints)], completionHandler: completion) } - - func evaluateDaitaSettingsCompatibility(_ settings: DAITASettings) -> DAITASettingsCompatibilityError? { - guard settings.daitaState.isEnabled else { return nil } - - var tunnelSettings = tunnelSettings - tunnelSettings.daita = settings - - let selectedRelays = try? tunnelManager.selectRelays(tunnelSettings: tunnelSettings) - let multihopEnabled = tunnelSettings.tunnelMultihopState.isEnabled - - return if multihopEnabled { - selectedRelays?.entry == nil ? .multihop : nil - } else { - selectedRelays?.exit == nil ? .singlehop : nil - } - } } extension VPNSettingsInteractor: RelayCacheTrackerObserver { diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsPromptAlertItem.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsPromptAlertItem.swift deleted file mode 100644 index 74cc90b8d836..000000000000 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsPromptAlertItem.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// VPNSettingsPromptAlertItem.swift -// MullvadVPN -// -// Created by Mojgan on 2024-09-16. -// Copyright © 2024 Mullvad VPN AB. All rights reserved. -// - -import Foundation -enum VPNSettingsPromptAlertItem: CustomStringConvertible { - case daitaSettingIncompatibleWithSinglehop - case daitaSettingIncompatibleWithMultihop - - var description: String { - switch self { - case .daitaSettingIncompatibleWithSinglehop: - """ - DAITA isn’t available on the current server. After enabling, please go to the Switch \ - location view and select a location that supports DAITA. - Attention: Since this increases your total network traffic, be cautious if you have a \ - limited data plan. It can also negatively impact your network speed and battery usage. - """ - case .daitaSettingIncompatibleWithMultihop: - """ - DAITA isn’t available on the current entry server. After enabling, please go to the Switch \ - location view and select an entry location that supports DAITA. - Attention: Since this increases your total network traffic, be cautious if you have a \ - limited data plan. It can also negatively impact your network speed and battery usage. - """ - } - } -} diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift index 32facd0ecab8..c86db1b9d09b 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift @@ -123,51 +123,4 @@ extension VPNSettingsViewController: VPNSettingsDataSourceDelegate { func didSelectWireGuardPort(_ port: UInt16?) { interactor.setPort(port) } - - func didAttemptToChangeDaitaSettings(_ settings: DAITASettings) -> DAITASettingsCompatibilityError? { - interactor.evaluateDaitaSettingsCompatibility(settings) - } - - func showPrompt( - for item: VPNSettingsPromptAlertItem, - onSave: @escaping () -> Void, - onDiscard: @escaping () -> Void - ) { - let presentation = AlertPresentation( - id: "vpn-settings-content-blockers-alert", - accessibilityIdentifier: .daitaPromptAlert, - icon: .info, - message: NSLocalizedString( - "VPN_SETTINGS_VPN_DAITA_ENABLE_TEXT", - tableName: "DAITA", - value: item.description, - comment: "" - ), - buttons: [ - AlertAction( - title: NSLocalizedString( - "VPN_SETTINGS_VPN_DAITA_OK_ACTION", - tableName: "DAITA", - value: "Enable anyway", - comment: "" - ), - style: .default, - accessibilityId: .daitaConfirmAlertEnableButton, - handler: { onSave() } - ), - AlertAction( - title: NSLocalizedString( - "VPN_SETTINGS_VPN_DAITA_CANCEL_ACTION", - tableName: "DAITA", - value: "Back", - comment: "" - ), - style: .default, - handler: { onDiscard() } - ), - ] - ) - - alertPresenter.showAlert(presentation: presentation, animated: true) - } } diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift index 1c125e19fd48..457a8928cae0 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift @@ -101,7 +101,6 @@ struct VPNSettingsViewModel: Equatable { private(set) var quantumResistance: TunnelQuantumResistance private(set) var multihopState: MultihopState - private(set) var daitaSettings: DAITASettings static let defaultWireGuardPorts: [UInt16] = [51820, 53] @@ -191,10 +190,6 @@ struct VPNSettingsViewModel: Equatable { multihopState = newState } - mutating func setDAITASettings(_ newSettings: DAITASettings) { - daitaSettings = newSettings - } - /// Precondition for enabling Custom DNS. var customDNSPrecondition: CustomDNSPrecondition { if blockAdvertising || blockTracking || blockMalware || @@ -251,7 +246,6 @@ struct VPNSettingsViewModel: Equatable { quantumResistance = tunnelSettings.tunnelQuantumResistance multihopState = tunnelSettings.tunnelMultihopState - daitaSettings = tunnelSettings.daita } /// Produce merged view model keeping entry `identifier` for matching DNS entries.