diff options
| author | Bug Magnet <marco.nikic@mullvad.net> | 2025-05-23 13:37:40 +0200 |
|---|---|---|
| committer | Bug Magnet <marco.nikic@mullvad.net> | 2025-05-23 13:37:40 +0200 |
| commit | b10275936d3a987220082933c4057c3d72ca2647 (patch) | |
| tree | f638b0016563c9b8e62ee82bd76127d1c6171003 | |
| parent | 976b143a220b1b65a2a4d7004cd40688c25da79b (diff) | |
| parent | 6b4547b0993b13595bdd1e5857b04ccb4903df52 (diff) | |
| download | mullvadvpn-b10275936d3a987220082933c4057c3d72ca2647.tar.xz mullvadvpn-b10275936d3a987220082933c4057c3d72ca2647.zip | |
Merge branch 'adopt-api-access-enabledisable-logic-like-android-does-ios-1063'
8 files changed, 88 insertions, 2 deletions
diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/SwitchCellContentConfiguration.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/SwitchCellContentConfiguration.swift index 54794917a8..8a68a72164 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/SwitchCellContentConfiguration.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/SwitchCellContentConfiguration.swift @@ -32,6 +32,9 @@ struct SwitchCellContentConfiguration: UIContentConfiguration, Equatable { /// Content view layout margins. var directionalLayoutMargins: NSDirectionalEdgeInsets = UIMetrics.SettingsCell.apiAccessInsetLayoutMargins + /// Whether the toggle is enabled or disabled + var isEnabled = true + func makeContentView() -> UIView & UIContentView { return SwitchCellContentView(configuration: self) } diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/SwitchCellContentView.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/SwitchCellContentView.swift index db77e8b8d7..cbf50170aa 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/SwitchCellContentView.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/SwitchCellContentView.swift @@ -77,6 +77,7 @@ class SwitchCellContentView: UIView, UIContentView, UITextFieldDelegate { switchContainer.control.isOn = actualConfiguration.isOn switchContainer.transform = CGAffineTransform(scaleX: 0.85, y: 0.85) switchContainer.setAccessibilityIdentifier(actualConfiguration.accessibilityIdentifier) + switchContainer.isEnabled = actualConfiguration.isEnabled } private func addSubviews() { diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodInteractor.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodInteractor.swift index ee33e376d4..7770c3b2e1 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodInteractor.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodInteractor.swift @@ -15,14 +15,40 @@ struct EditAccessMethodInteractor: EditAccessMethodInteractorProtocol { let repository: AccessMethodRepositoryProtocol let proxyConfigurationTester: ProxyConfigurationTesterProtocol + init( + subject: CurrentValueSubject<AccessMethodViewModel, Never>, + repository: AccessMethodRepositoryProtocol, + proxyConfigurationTester: ProxyConfigurationTesterProtocol + ) { + self.subject = subject + self.repository = repository + self.proxyConfigurationTester = proxyConfigurationTester + checkIfSwitchCanBeToggled() + } + + // The access method can only be disabled if at least one other method is enabled + private func checkIfSwitchCanBeToggled() { + let enabledMethodsCount = repository.fetchAll().count { $0.isEnabled } + if enabledMethodsCount < 2 { + subject.value.canBeToggled = !subject.value.isEnabled + } else { + subject.value.canBeToggled = true + } + } + func saveAccessMethod() { guard let persistentMethod = try? subject.value.intoPersistentAccessMethod() else { return } repository.save(persistentMethod) + checkIfSwitchCanBeToggled() } func deleteAccessMethod() { repository.delete(id: subject.value.id) + // Enable direct access if all methods are disabled + if repository.fetchAll().count(where: { $0.isEnabled }) == 0 { + repository.save(repository.directAccess) + } } func startProxyConfigurationTest(_ completion: (@Sendable (Bool) -> Void)?) { diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodSectionIdentifier.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodSectionIdentifier.swift index 79f9bbae80..be0a05b4d2 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodSectionIdentifier.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodSectionIdentifier.swift @@ -26,8 +26,14 @@ enum EditAccessMethodSectionIdentifier: Hashable { value: "Performs a connection test to a Mullvad API server via this access method.", comment: "" ) - - case .enableMethod, .methodSettings, .cancelTest, .testingStatus, .deleteMethod: + case .enableMethod: + NSLocalizedString( + "METHOD_FOOTER", + tableName: "APIAccess", + value: "At least one method needs to be enabled.", + comment: "" + ) + case .methodSettings, .cancelTest, .testingStatus, .deleteMethod: nil } } diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewController.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewController.swift index ca05439678..4d0440fb07 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewController.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewController.swift @@ -140,6 +140,9 @@ extension EditAccessMethodViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { guard let sectionIdentifier = dataSource?.snapshot().sectionIdentifiers[section] else { return nil } + if sectionIdentifier == .enableMethod && subject.value.canBeToggled { + return nil + } guard let sectionFooterText = sectionIdentifier.sectionFooter else { return nil } guard let headerView = tableView @@ -250,6 +253,8 @@ extension EditAccessMethodViewController: UITableViewDelegate { self?.onSave() } } + + contentConfiguration.isEnabled = subject.value.canBeToggled cell.contentConfiguration = contentConfiguration } diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift index e6b98f96d0..56a31ce75d 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift @@ -63,6 +63,9 @@ struct AccessMethodViewModel: Identifiable { /// The flag indicating whether configuration is enabled. var isEnabled = true + /// The flag indicating whether configuration is enabled. + var canBeToggled = true + /// The status of testing the entered proxy configuration. var testingStatus: TestingStatus = .initial diff --git a/ios/MullvadVPNUITests/AccessMethodsTests.swift b/ios/MullvadVPNUITests/AccessMethodsTests.swift index 37ec9e832d..30f8cdf48c 100644 --- a/ios/MullvadVPNUITests/AccessMethodsTests.swift +++ b/ios/MullvadVPNUITests/AccessMethodsTests.swift @@ -56,4 +56,41 @@ class AccessMethodsTests: LoggedOutUITestCase { .tapTestMethodButton() .verifyTestStatus(.reachable) } + + func testCanNotDisableLast() throws { + HeaderBar(app) + .tapSettingsButton() + + SettingsPage(app) + .tapAPIAccessCell() + + APIAccessPage(app) + .getAccessMethodCell( + accessibilityId: AccessibilityIdentifier.accessMethodDirectCell + ) + .tap() + + EditAccessMethodPage(app) + .tapEnableMethodSwitch() + .tapBackButton() + + APIAccessPage(app) + .getAccessMethodCell( + accessibilityId: AccessibilityIdentifier.accessMethodBridgesCell + ) + .tap() + + EditAccessMethodPage(app) + .tapEnableMethodSwitch() + .tapBackButton() + + APIAccessPage(app) + .getAccessMethodCell( + accessibilityId: AccessibilityIdentifier.accessMethodEncryptedDNSCell + ) + .tap() + + EditAccessMethodPage(app) + .verifySwitchDisabled() + } } diff --git a/ios/MullvadVPNUITests/Pages/EditAccessMethodPage.swift b/ios/MullvadVPNUITests/Pages/EditAccessMethodPage.swift index 0941f8edab..bebcf92d12 100644 --- a/ios/MullvadVPNUITests/Pages/EditAccessMethodPage.swift +++ b/ios/MullvadVPNUITests/Pages/EditAccessMethodPage.swift @@ -59,4 +59,9 @@ class EditAccessMethodPage: Page { backButton.tap() return self } + + @discardableResult func verifySwitchDisabled() -> Self { + XCTAssertFalse(app.switches[AccessibilityIdentifier.accessMethodEnableSwitch].isEnabled) + return self + } } |
