Skip to content

Commit efa6e2c

Browse files
committed
Add the DAITA page to the root of the settings page
1 parent 31648b9 commit efa6e2c

20 files changed

+340
-362
lines changed

ios/MullvadSettings/MultihopSettings.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import Foundation
1010
import MullvadTypes
1111

12-
/// Whether Multihop is enabled
12+
/// Whether multihop is enabled.
1313
public enum MultihopState: Codable {
1414
case on
1515
case off

ios/MullvadVPN.xcodeproj/project.pbxproj

+13-17
Original file line numberDiff line numberDiff line change
@@ -565,11 +565,10 @@
565565
7A8A190E2CEB77C1000BCB5B /* SettingsRowViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A190D2CEB77B7000BCB5B /* SettingsRowViewFooter.swift */; };
566566
7A8A19102CEE391B000BCB5B /* RowSeparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A190F2CEE3918000BCB5B /* RowSeparator.swift */; };
567567
7A8A19122CEF1E68000BCB5B /* SettingsInfoContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19112CEF1E58000BCB5B /* SettingsInfoContainerView.swift */; };
568-
7A8A19142CEF2548000BCB5B /* DAITATunnelSettingsObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19132CEF2527000BCB5B /* DAITATunnelSettingsObserver.swift */; };
569-
7A8A19162CEF269E000BCB5B /* MultihopTunnelSettingsObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19152CEF2696000BCB5B /* MultihopTunnelSettingsObserver.swift */; };
570-
7A8A19182CEF27AB000BCB5B /* DAITATunnelSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19172CEF279C000BCB5B /* DAITATunnelSettingsViewModel.swift */; };
568+
7A8A19142CEF2548000BCB5B /* DAITATunnelSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19132CEF2527000BCB5B /* DAITATunnelSettingsViewModel.swift */; };
569+
7A8A19162CEF269E000BCB5B /* MultihopTunnelSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19152CEF2696000BCB5B /* MultihopTunnelSettingsViewModel.swift */; };
571570
7A8A191A2CEF41AF000BCB5B /* GroupedRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19192CEF41AC000BCB5B /* GroupedRowView.swift */; };
572-
7A8A191C2CEF55E3000BCB5B /* MultihopTunnelSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A191B2CEF55DA000BCB5B /* MultihopTunnelSettingsViewModel.swift */; };
571+
7A8A191E2CEF5CF2000BCB5B /* TunnelSettingsObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A191D2CEF5CDF000BCB5B /* TunnelSettingsObservable.swift */; };
573572
7A9BE5A22B8F88C500E2A7D0 /* LocationNodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9BE5A12B8F88C500E2A7D0 /* LocationNodeTests.swift */; };
574573
7A9BE5A32B8F89B900E2A7D0 /* LocationNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389F72B864CDF008E77E1 /* LocationNode.swift */; };
575574
7A9BE5A52B90760C00E2A7D0 /* CustomListsDataSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9BE5A42B90760C00E2A7D0 /* CustomListsDataSourceTests.swift */; };
@@ -1921,11 +1920,10 @@
19211920
7A8A190D2CEB77B7000BCB5B /* SettingsRowViewFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRowViewFooter.swift; sourceTree = "<group>"; };
19221921
7A8A190F2CEE3918000BCB5B /* RowSeparator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowSeparator.swift; sourceTree = "<group>"; };
19231922
7A8A19112CEF1E58000BCB5B /* SettingsInfoContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsInfoContainerView.swift; sourceTree = "<group>"; };
1924-
7A8A19132CEF2527000BCB5B /* DAITATunnelSettingsObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DAITATunnelSettingsObserver.swift; sourceTree = "<group>"; };
1925-
7A8A19152CEF2696000BCB5B /* MultihopTunnelSettingsObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultihopTunnelSettingsObserver.swift; sourceTree = "<group>"; };
1926-
7A8A19172CEF279C000BCB5B /* DAITATunnelSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DAITATunnelSettingsViewModel.swift; sourceTree = "<group>"; };
1923+
7A8A19132CEF2527000BCB5B /* DAITATunnelSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DAITATunnelSettingsViewModel.swift; sourceTree = "<group>"; };
1924+
7A8A19152CEF2696000BCB5B /* MultihopTunnelSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultihopTunnelSettingsViewModel.swift; sourceTree = "<group>"; };
19271925
7A8A19192CEF41AC000BCB5B /* GroupedRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupedRowView.swift; sourceTree = "<group>"; };
1928-
7A8A191B2CEF55DA000BCB5B /* MultihopTunnelSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultihopTunnelSettingsViewModel.swift; sourceTree = "<group>"; };
1926+
7A8A191D2CEF5CDF000BCB5B /* TunnelSettingsObservable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsObservable.swift; sourceTree = "<group>"; };
19291927
7A9BE5A12B8F88C500E2A7D0 /* LocationNodeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationNodeTests.swift; sourceTree = "<group>"; };
19301928
7A9BE5A42B90760C00E2A7D0 /* CustomListsDataSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListsDataSourceTests.swift; sourceTree = "<group>"; };
19311929
7A9BE5A82B90806800E2A7D0 /* CustomListsRepositoryStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListsRepositoryStub.swift; sourceTree = "<group>"; };
@@ -2887,7 +2885,6 @@
28872885
4424CDD12CDBD457009D8C9F /* SwiftUI components */,
28882886
4422C06F2CCFF6520001A385 /* Obfuscation */,
28892887
7A9FA1432A2E3FE5000B728D /* CheckableSettingsCell.swift */,
2890-
F041BE4E2C983C2B0083EC28 /* DAITASettingsPromptItem.swift */,
28912888
7A1A264A2A29D65E00B978AA /* SelectableSettingsCell.swift */,
28922889
7A27E3CE2CBD4A830088BCFF /* SelectableSettingsDetailsCell.swift */,
28932890
5819C2162729595500D6EC38 /* SettingsAddDNSEntryCell.swift */,
@@ -3825,6 +3822,7 @@
38253822
7A9CCCAD2A96302800DD6A34 /* SettingsCoordinator.swift */,
38263823
7A6389EC2B7FADA1008E77E1 /* SettingsFieldValidationErrorConfiguration.swift */,
38273824
7A6389EA2B7FAD7A008E77E1 /* SettingsFieldValidationErrorContentView.swift */,
3825+
7A8A191D2CEF5CDF000BCB5B /* TunnelSettingsObservable.swift */,
38283826
);
38293827
path = Settings;
38303828
sourceTree = "<group>";
@@ -3978,8 +3976,7 @@
39783976
7A8A18F72CE34E8F000BCB5B /* Multihop */ = {
39793977
isa = PBXGroup;
39803978
children = (
3981-
7A8A19152CEF2696000BCB5B /* MultihopTunnelSettingsObserver.swift */,
3982-
7A8A191B2CEF55DA000BCB5B /* MultihopTunnelSettingsViewModel.swift */,
3979+
7A8A19152CEF2696000BCB5B /* MultihopTunnelSettingsViewModel.swift */,
39833980
7A8A18F82CE34E9F000BCB5B /* SettingsMultihopView.swift */,
39843981
);
39853982
path = Multihop;
@@ -4000,8 +3997,8 @@
40003997
7A8A19082CE5FFD7000BCB5B /* DAITA */ = {
40013998
isa = PBXGroup;
40023999
children = (
4003-
7A8A19132CEF2527000BCB5B /* DAITATunnelSettingsObserver.swift */,
4004-
7A8A19172CEF279C000BCB5B /* DAITATunnelSettingsViewModel.swift */,
4000+
F041BE4E2C983C2B0083EC28 /* DAITASettingsPromptItem.swift */,
4001+
7A8A19132CEF2527000BCB5B /* DAITATunnelSettingsViewModel.swift */,
40054002
7A8A19092CE5FFDF000BCB5B /* SettingsDAITAView.swift */,
40064003
);
40074004
path = DAITA;
@@ -5781,7 +5778,7 @@
57815778
7A9CCCB72A96302800DD6A34 /* RevokedCoordinator.swift in Sources */,
57825779
7A6389F82B864CDF008E77E1 /* LocationNode.swift in Sources */,
57835780
587D96742886D87C00CD8F1C /* DeviceManagementContentView.swift in Sources */,
5784-
7A8A19142CEF2548000BCB5B /* DAITATunnelSettingsObserver.swift in Sources */,
5781+
7A8A19142CEF2548000BCB5B /* DAITATunnelSettingsViewModel.swift in Sources */,
57855782
7A8A18F92CE34EA8000BCB5B /* SettingsMultihopView.swift in Sources */,
57865783
44BB5F972BE527F4002520EB /* TunnelState+UI.swift in Sources */,
57875784
7A11DD0B2A9495D400098CD8 /* AppRoutes.swift in Sources */,
@@ -5932,7 +5929,6 @@
59325929
F0EF50D52A949F8E0031E8DF /* ChangeLogViewModel.swift in Sources */,
59335930
F0E8E4BB2A56C9F100ED26A3 /* WelcomeInteractor.swift in Sources */,
59345931
7A6000F62B60092F001CF0D9 /* AccessMethodViewModelEditing.swift in Sources */,
5935-
7A8A191C2CEF55E3000BCB5B /* MultihopTunnelSettingsViewModel.swift in Sources */,
59365932
5878A27729093A4F0096FC88 /* StorePaymentBlockObserver.swift in Sources */,
59375933
58EF87572B16330B00C098B2 /* ProxyConfigurationTester.swift in Sources */,
59385934
5827B0A62B0F39E900CCBBA1 /* EditAccessMethodInteractor.swift in Sources */,
@@ -5960,6 +5956,7 @@
59605956
7AB2B6712BA1EB8C00B03E3B /* ListCustomListCoordinator.swift in Sources */,
59615957
582BB1AF229566420055B6EF /* SettingsCell.swift in Sources */,
59625958
7AF9BE8E2A331C7B00DBFEDB /* RelayFilterViewModel.swift in Sources */,
5959+
7A8A191E2CEF5CF2000BCB5B /* TunnelSettingsObservable.swift in Sources */,
59635960
58F3C0A4249CB069003E76BE /* HeaderBarView.swift in Sources */,
59645961
5864AF0829C78849005B0CD9 /* CellFactoryProtocol.swift in Sources */,
59655962
7A6389E22B7E3BD6008E77E1 /* CustomListInteractor.swift in Sources */,
@@ -5968,7 +5965,6 @@
59685965
58CEB2F32AFD0BA100E6E088 /* TextCellContentView.swift in Sources */,
59695966
7A6389E72B7E42BE008E77E1 /* CustomListViewController.swift in Sources */,
59705967
586C0D7C2B03BDD100E7CDD7 /* AccessMethodViewModel.swift in Sources */,
5971-
7A8A19182CEF27AB000BCB5B /* DAITATunnelSettingsViewModel.swift in Sources */,
59725968
587A01FC23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift in Sources */,
59735969
7A6F2FA72AFBB9AE006D0856 /* AccountExpiry.swift in Sources */,
59745970
5819C2172729595500D6EC38 /* SettingsAddDNSEntryCell.swift in Sources */,
@@ -6002,7 +5998,7 @@
60025998
5892A45E265FABFF00890742 /* EmptyTableViewHeaderFooterView.swift in Sources */,
60035999
580909D32876D09A0078138D /* RevokedDeviceViewController.swift in Sources */,
60046000
58FF9FF02B07C4D300E4C97D /* PersistentAccessMethod+ViewModel.swift in Sources */,
6005-
7A8A19162CEF269E000BCB5B /* MultihopTunnelSettingsObserver.swift in Sources */,
6001+
7A8A19162CEF269E000BCB5B /* MultihopTunnelSettingsViewModel.swift in Sources */,
60066002
58CEB2FD2AFD19D300E6E088 /* UITableView+ReuseIdentifier.swift in Sources */,
60076003
F0FADDEA2BE90AAA000D0B02 /* LaunchArguments.swift in Sources */,
60086004
5835B7CC233B76CB0096D79F /* TunnelManager.swift in Sources */,

ios/MullvadVPN/Coordinators/Settings/DAITA/DAITATunnelSettingsObserver.swift

-33
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,107 @@
11
//
2-
// DAITATunnelSettingsObservable.swift
2+
// DAITATunnelSettingsViewModel.swift
33
// MullvadVPN
44
//
55
// Created by Jon Petersson on 2024-11-21.
66
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
77
//
88

9-
import Foundation
9+
import MullvadREST
1010
import MullvadSettings
1111

12-
protocol DAITATunnelSettingsObservable: ObservableObject {
13-
var value: DAITASettings { get set }
12+
class DAITATunnelSettingsViewModel: TunnelSettingsObserver, ObservableObject {
13+
typealias TunnelSetting = DAITASettings
14+
15+
let tunnelManager: TunnelManager
16+
var tunnelObserver: TunnelObserver?
17+
18+
var didFailDAITAValidation: (((item: DAITASettingsPromptItem, setting: DAITASettings)) -> Void)?
19+
20+
var value: DAITASettings {
21+
willSet {
22+
guard newValue != value else { return }
23+
24+
objectWillChange.send()
25+
tunnelManager.updateSettings([.daita(newValue)])
26+
}
27+
}
28+
29+
required init(tunnelManager: TunnelManager) {
30+
self.tunnelManager = tunnelManager
31+
value = tunnelManager.settings.daita
32+
33+
tunnelObserver = TunnelBlockObserver(didUpdateTunnelSettings: { [weak self] _, newSettings in
34+
self?.value = newSettings.daita
35+
})
36+
}
37+
38+
func evaluate(setting: DAITASettings) {
39+
if let error = evaluateDaitaSettingsCompatibility(setting) {
40+
let promptItem = promptItem(from: error, setting: setting)
41+
42+
didFailDAITAValidation?((item: promptItem, setting: setting))
43+
return
44+
}
45+
46+
value = setting
47+
}
48+
}
49+
50+
extension DAITATunnelSettingsViewModel {
51+
private func promptItem(
52+
from error: DAITASettingsCompatibilityError,
53+
setting: DAITASettings
54+
) -> DAITASettingsPromptItem {
55+
let promptItemSetting: DAITASettingsPromptItem.Setting = if setting.daitaState != value.daitaState {
56+
.daita
57+
} else {
58+
.directOnly
59+
}
60+
61+
var promptItem: DAITASettingsPromptItem
62+
63+
switch error {
64+
case .singlehop:
65+
promptItem = .daitaSettingIncompatibleWithSinglehop(promptItemSetting)
66+
case .multihop:
67+
promptItem = .daitaSettingIncompatibleWithMultihop(promptItemSetting)
68+
}
69+
70+
return promptItem
71+
}
72+
73+
private func evaluateDaitaSettingsCompatibility(_ settings: DAITASettings) -> DAITASettingsCompatibilityError? {
74+
guard settings.daitaState.isEnabled else { return nil }
75+
76+
var tunnelSettings = tunnelManager.settings
77+
tunnelSettings.daita = settings
78+
79+
var compatibilityError: DAITASettingsCompatibilityError?
80+
81+
do {
82+
_ = try tunnelManager.selectRelays(tunnelSettings: tunnelSettings)
83+
} catch let error as NoRelaysSatisfyingConstraintsError where error.reason == .noDaitaRelaysFound {
84+
// Return error if no relays could be selected due to DAITA constraints.
85+
compatibilityError = tunnelSettings.tunnelMultihopState.isEnabled ? .multihop : .singlehop
86+
} catch _ as NoRelaysSatisfyingConstraintsError {
87+
// Even if the constraints error is not DAITA specific, if both DAITA and Direct only are enabled,
88+
// we should return a DAITA related error since the current settings would have resulted in the
89+
// relay selector not being able to select a DAITA relay anyway.
90+
if settings.isDirectOnly {
91+
compatibilityError = tunnelSettings.tunnelMultihopState.isEnabled ? .multihop : .singlehop
92+
}
93+
} catch {}
94+
95+
return compatibilityError
96+
}
1497
}
1598

16-
class MockDAITATunnelSettingsViewModel: DAITATunnelSettingsObservable {
99+
class MockDAITATunnelSettingsViewModel: TunnelSettingsObservable {
17100
@Published var value: DAITASettings
18101

19102
init(daitaSettings: DAITASettings = DAITASettings()) {
20103
value = daitaSettings
21104
}
22-
}
23105

24-
class DAITATunnelSettingsViewModel: DAITATunnelSettingsObserver, DAITATunnelSettingsObservable {}
106+
func evaluate(setting: MullvadSettings.DAITASettings) {}
107+
}

0 commit comments

Comments
 (0)