summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJon Petersson <jon.petersson@kvadrat.se>2024-10-02 12:10:52 +0200
committerEmīls <emils@mullvad.net>2024-10-02 14:16:55 +0200
commit47a083404dbadedfcb6570c283aff0808f80f70e (patch)
tree319407517eae990526c116f8ad3053f8815c4600
parente41213478a35a54d4fce0d82ad52f565ef69b0a4 (diff)
downloadmullvadvpn-47a083404dbadedfcb6570c283aff0808f80f70e.tar.xz
mullvadvpn-47a083404dbadedfcb6570c283aff0808f80f70e.zip
Add info modals to default access methods screens
-rw-r--r--ios/MullvadMockData/MullvadREST/AccessMethodRepository+Stub.swift4
-rw-r--r--ios/MullvadSettings/AccessMethodRepository.swift10
-rw-r--r--ios/MullvadSettings/InfoHeaderConfig.swift17
-rw-r--r--ios/MullvadSettings/InfoModalConfig.swift19
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj22
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodCoordinator.swift18
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodSectionIdentifier.swift10
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewController.swift55
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewControllerDelegate.swift5
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodHeaderView.swift115
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodViewController.swift33
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift163
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift17
-rw-r--r--ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift1
-rw-r--r--ios/MullvadVPN/Views/InfoHeaderView.swift (renamed from ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideHeaderView.swift)35
15 files changed, 343 insertions, 181 deletions
diff --git a/ios/MullvadMockData/MullvadREST/AccessMethodRepository+Stub.swift b/ios/MullvadMockData/MullvadREST/AccessMethodRepository+Stub.swift
index c21941f4af..7dbf88dbb5 100644
--- a/ios/MullvadMockData/MullvadREST/AccessMethodRepository+Stub.swift
+++ b/ios/MullvadMockData/MullvadREST/AccessMethodRepository+Stub.swift
@@ -32,4 +32,8 @@ public struct AccessMethodRepositoryStub: AccessMethodRepositoryDataSource {
public func fetchLastReachable() -> PersistentAccessMethod {
directAccess
}
+
+ public func infoHeaderConfig(for id: UUID) -> InfoHeaderConfig? {
+ nil
+ }
}
diff --git a/ios/MullvadSettings/AccessMethodRepository.swift b/ios/MullvadSettings/AccessMethodRepository.swift
index 1ffa3c68e1..3aa2242533 100644
--- a/ios/MullvadSettings/AccessMethodRepository.swift
+++ b/ios/MullvadSettings/AccessMethodRepository.swift
@@ -12,24 +12,28 @@ import MullvadLogging
import MullvadTypes
public class AccessMethodRepository: AccessMethodRepositoryProtocol {
+ public static let directId = UUID(uuidString: "C9DB7457-2A55-42C3-A926-C07F82131994")!
+ public static let bridgeId = UUID(uuidString: "8586E75A-CA7B-4432-B70D-EE65F3F95084")!
+ public static let encryptedDNSId = UUID(uuidString: "831CB1F8-1829-42DD-B9DC-82902F298EC0")!
+
private let logger = Logger(label: "AccessMethodRepository")
private let direct = PersistentAccessMethod(
- id: UUID(uuidString: "C9DB7457-2A55-42C3-A926-C07F82131994")!,
+ id: AccessMethodRepository.directId,
name: "Direct",
isEnabled: true,
proxyConfiguration: .direct
)
private let bridge = PersistentAccessMethod(
- id: UUID(uuidString: "8586E75A-CA7B-4432-B70D-EE65F3F95084")!,
+ id: AccessMethodRepository.bridgeId,
name: "Mullvad bridges",
isEnabled: true,
proxyConfiguration: .bridges
)
private let encryptedDNS = PersistentAccessMethod(
- id: UUID(uuidString: "831CB1F8-1829-42DD-B9DC-82902F298EC0")!,
+ id: AccessMethodRepository.encryptedDNSId,
name: "Encrypted DNS proxy",
isEnabled: true,
proxyConfiguration: .encryptedDNS
diff --git a/ios/MullvadSettings/InfoHeaderConfig.swift b/ios/MullvadSettings/InfoHeaderConfig.swift
new file mode 100644
index 0000000000..43ad7bc7e7
--- /dev/null
+++ b/ios/MullvadSettings/InfoHeaderConfig.swift
@@ -0,0 +1,17 @@
+//
+// InfoHeaderConfig.swift
+// MullvadVPN
+//
+// Created by Jon Petersson on 2024-10-01.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+public struct InfoHeaderConfig {
+ public let body: String
+ public let link: String
+
+ public init(body: String, link: String) {
+ self.body = body
+ self.link = link
+ }
+}
diff --git a/ios/MullvadSettings/InfoModalConfig.swift b/ios/MullvadSettings/InfoModalConfig.swift
new file mode 100644
index 0000000000..e2db6ecfd7
--- /dev/null
+++ b/ios/MullvadSettings/InfoModalConfig.swift
@@ -0,0 +1,19 @@
+//
+// InfoModalConfig.swift
+// MullvadVPN
+//
+// Created by Jon Petersson on 2024-10-02.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+public struct InfoModalConfig {
+ public let header: String
+ public let preamble: String
+ public let body: [String]
+
+ public init(header: String, preamble: String, body: [String]) {
+ self.header = header
+ self.preamble = preamble
+ self.body = body
+ }
+}
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 82506832af..5942519486 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -400,7 +400,6 @@
58EF581125D69DB400AEBA94 /* StatusImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF581025D69DB400AEBA94 /* StatusImageView.swift */; };
58EF87572B16330B00C098B2 /* ProxyConfigurationTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF87562B16330B00C098B2 /* ProxyConfigurationTester.swift */; };
58EF875D2B1638BF00C098B2 /* ProxyConfigurationTesterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF875C2B1638BF00C098B2 /* ProxyConfigurationTesterProtocol.swift */; };
- 58EFC76A2AFAC3B800E9F4CB /* ListAccessMethodHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EFC7692AFAC3B800E9F4CB /* ListAccessMethodHeaderView.swift */; };
58EFC76E2AFB3BDA00E9F4CB /* ListAccessMethodCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EFC76D2AFB3BDA00E9F4CB /* ListAccessMethodCoordinator.swift */; };
58EFC7712AFB45E500E9F4CB /* SettingsChildCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EFC7702AFB45E500E9F4CB /* SettingsChildCoordinator.swift */; };
58EFC7752AFB4CEF00E9F4CB /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EFC7742AFB4CEF00E9F4CB /* AboutViewController.swift */; };
@@ -466,7 +465,6 @@
7A1A26492A29D48A00B978AA /* RelayFilterCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A26482A29D48A00B978AA /* RelayFilterCellFactory.swift */; };
7A21DACF2A30AA3700A787A9 /* UITextField+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */; };
7A28826A2BA8336600FD9F20 /* VPNSettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2882692BA8336600FD9F20 /* VPNSettingsCoordinator.swift */; };
- 7A28826D2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28826C2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift */; };
7A2960F62A963F7500389B82 /* AlertCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2960F52A963F7500389B82 /* AlertCoordinator.swift */; };
7A2960FD2A964BB700389B82 /* AlertPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2960FC2A964BB700389B82 /* AlertPresentation.swift */; };
7A307AD92A8CD8DA0017618B /* Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A307AD82A8CD8DA0017618B /* Duration.swift */; };
@@ -573,6 +571,9 @@
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 */; };
+ 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 */; };
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 */; };
@@ -1739,7 +1740,6 @@
58EF87562B16330B00C098B2 /* ProxyConfigurationTester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyConfigurationTester.swift; sourceTree = "<group>"; };
58EF875A2B16385400C098B2 /* AccessMethodRepositoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessMethodRepositoryProtocol.swift; sourceTree = "<group>"; };
58EF875C2B1638BF00C098B2 /* ProxyConfigurationTesterProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyConfigurationTesterProtocol.swift; sourceTree = "<group>"; };
- 58EFC7692AFAC3B800E9F4CB /* ListAccessMethodHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListAccessMethodHeaderView.swift; sourceTree = "<group>"; };
58EFC76D2AFB3BDA00E9F4CB /* ListAccessMethodCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListAccessMethodCoordinator.swift; sourceTree = "<group>"; };
58EFC7702AFB45E500E9F4CB /* SettingsChildCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsChildCoordinator.swift; sourceTree = "<group>"; };
58EFC7742AFB4CEF00E9F4CB /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
@@ -1796,7 +1796,6 @@
7A1A264A2A29D65E00B978AA /* SelectableSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableSettingsCell.swift; sourceTree = "<group>"; };
7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Appearance.swift"; sourceTree = "<group>"; };
7A2882692BA8336600FD9F20 /* VPNSettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNSettingsCoordinator.swift; sourceTree = "<group>"; };
- 7A28826C2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideHeaderView.swift; sourceTree = "<group>"; };
7A2960F52A963F7500389B82 /* AlertCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertCoordinator.swift; sourceTree = "<group>"; };
7A2960FC2A964BB700389B82 /* AlertPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertPresentation.swift; sourceTree = "<group>"; };
7A307AD82A8CD8DA0017618B /* Duration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Duration.swift; sourceTree = "<group>"; };
@@ -1889,6 +1888,9 @@
7A9CCCB02A96302800DD6A34 /* SafariCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafariCoordinator.swift; sourceTree = "<group>"; };
7A9CCCB12A96302800DD6A34 /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplicationCoordinator.swift; sourceTree = "<group>"; };
7A9CCCB22A96302800DD6A34 /* TunnelCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelCoordinator.swift; sourceTree = "<group>"; };
+ 7A9F29382CABFAEC005F2089 /* InfoHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoHeaderView.swift; sourceTree = "<group>"; };
+ 7A9F293A2CAC4420005F2089 /* InfoHeaderConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoHeaderConfig.swift; sourceTree = "<group>"; };
+ 7A9F293C2CAD2FCF005F2089 /* InfoModalConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoModalConfig.swift; sourceTree = "<group>"; };
7A9FA1412A2E3306000B728D /* CheckboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxView.swift; sourceTree = "<group>"; };
7A9FA1432A2E3FE5000B728D /* CheckableSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckableSettingsCell.swift; sourceTree = "<group>"; };
7AA513852BC91C6B00D081A4 /* LogRotationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogRotationTests.swift; sourceTree = "<group>"; };
@@ -2901,6 +2903,7 @@
5892A45D265FABFF00890742 /* EmptyTableViewHeaderFooterView.swift */,
58FD5BF32428C67600112C88 /* InAppPurchaseButton.swift */,
F03580242A13842C00E5DAFD /* IncreasedHitButton.swift */,
+ 7A9F29382CABFAEC005F2089 /* InfoHeaderView.swift */,
7A5869942B32E9C700640D27 /* LinkButton.swift */,
58F19E34228C15BA00C7710B /* SpinnerActivityIndicatorView.swift */,
E1FD0DF428AA7CE400299DB4 /* StatusActivityView.swift */,
@@ -3301,6 +3304,8 @@
F0C13FE32C64F7CB00BD087D /* DAITASettings.swift */,
A92ECC2B2A7803A50052F1B1 /* DeviceState.swift */,
580F8B8528197958002E0998 /* DNSSettings.swift */,
+ 7A9F293A2CAC4420005F2089 /* InfoHeaderConfig.swift */,
+ 7A9F293C2CAD2FCF005F2089 /* InfoModalConfig.swift */,
7A5869B22B5697AC00640D27 /* IPOverride.swift */,
7A5869BA2B56EE9500640D27 /* IPOverrideRepository.swift */,
06410DFD292CE18F00AFC18C /* KeychainSettingsStore.swift */,
@@ -3569,7 +3574,6 @@
isa = PBXGroup;
children = (
58EFC76D2AFB3BDA00E9F4CB /* ListAccessMethodCoordinator.swift */,
- 58EFC7692AFAC3B800E9F4CB /* ListAccessMethodHeaderView.swift */,
588D7EDF2AF3A595005DF40A /* ListAccessMethodInteractor.swift */,
588D7EDB2AF3A55E005DF40A /* ListAccessMethodInteractorProtocol.swift */,
588D7EDD2AF3A585005DF40A /* ListAccessMethodItem.swift */,
@@ -3760,6 +3764,7 @@
58FF9FDE2B075AA700E4C97D /* Edit */ = {
isa = PBXGroup;
children = (
+ 5827B0992B0DC0CA00CCBBA1 /* MethodSettings */,
5827B08F2B0CAA0500CCBBA1 /* EditAccessMethodCoordinator.swift */,
5827B0A52B0F39E900CCBBA1 /* EditAccessMethodInteractor.swift */,
5827B0A32B0F38FD00CCBBA1 /* EditAccessMethodInteractorProtocol.swift */,
@@ -3767,7 +3772,6 @@
58FF9FE12B075BA600E4C97D /* EditAccessMethodSectionIdentifier.swift */,
58FF9FDF2B075ABC00E4C97D /* EditAccessMethodViewController.swift */,
5827B0A92B0F4C9100CCBBA1 /* EditAccessMethodViewControllerDelegate.swift */,
- 5827B0992B0DC0CA00CCBBA1 /* MethodSettings */,
5827B0A72B0F49EF00CCBBA1 /* ProxyConfigurationInteractorProtocol.swift */,
);
path = Edit;
@@ -3796,7 +3800,6 @@
isa = PBXGroup;
children = (
7A5869AA2B55527C00640D27 /* IPOverrideCoordinator.swift */,
- 7A28826C2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift */,
7AB4CCBA2B691BBB006037F5 /* IPOverrideInteractor.swift */,
7A5869BE2B57D0A100640D27 /* IPOverrideStatus.swift */,
7A5869C02B57D21A00640D27 /* IPOverrideStatusView.swift */,
@@ -5451,6 +5454,7 @@
58B2FDDF2AA71D5C003EB5C6 /* DNSSettings.swift in Sources */,
58B2FDE02AA71D5C003EB5C6 /* TunnelSettings.swift in Sources */,
A988DF2A2ADE880300D807EF /* TunnelSettingsV3.swift in Sources */,
+ 7A9F293D2CAD2FD5005F2089 /* InfoModalConfig.swift in Sources */,
58B2FDE42AA71D5C003EB5C6 /* SettingsManager.swift in Sources */,
F0164EBC2B482E430020268D /* AppStorage.swift in Sources */,
58B2FDE62AA71D5C003EB5C6 /* DeviceState.swift in Sources */,
@@ -5472,6 +5476,7 @@
58B2FDE22AA71D5C003EB5C6 /* StoredAccountData.swift in Sources */,
F0D7FF902B31E00B00E0FDE5 /* AccessMethodKind.swift in Sources */,
7A5869BC2B56EF3400640D27 /* IPOverrideRepository.swift in Sources */,
+ 7A9F293B2CAC4443005F2089 /* InfoHeaderConfig.swift in Sources */,
F0E61CAB2BF2911D000C4A95 /* MultihopSettings.swift in Sources */,
58B2FDE82AA71D5C003EB5C6 /* KeychainSettingsStore.swift in Sources */,
);
@@ -5640,6 +5645,7 @@
7A5869972B32EA4500640D27 /* AppButton.swift in Sources */,
586C0D8F2B03D88100E7CDD7 /* ProxyProtocolConfigurationItemIdentifier.swift in Sources */,
588527B2276B3F0700BAA373 /* LoadTunnelConfigurationOperation.swift in Sources */,
+ 7A9F29392CABFAFC005F2089 /* InfoHeaderView.swift in Sources */,
58DFF7D22B0256A300F864E0 /* MarkdownStylingOptions.swift in Sources */,
5867770E29096984006F721F /* OutOfTimeInteractor.swift in Sources */,
F03580252A13842C00E5DAFD /* IncreasedHitButton.swift in Sources */,
@@ -5806,7 +5812,6 @@
F0C6FA852A6A733700F521F0 /* InAppPurchaseInteractor.swift in Sources */,
58CEB2F92AFD136E00E6E088 /* UIBackgroundConfiguration+Extensions.swift in Sources */,
5878F50029CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift in Sources */,
- 7A28826D2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift in Sources */,
A98502032B627B120061901E /* LocalNetworkProbe.swift in Sources */,
7A6F2FA92AFD0842006D0856 /* CustomDNSDataSource.swift in Sources */,
58EF580B25D69D7A00AEBA94 /* ProblemReportSubmissionOverlayView.swift in Sources */,
@@ -5826,7 +5831,6 @@
5827B0A82B0F49EF00CCBBA1 /* ProxyConfigurationInteractorProtocol.swift in Sources */,
7A5869B92B56E7F000640D27 /* IPOverrideViewControllerDelegate.swift in Sources */,
586C0D7A2B039CE300E7CDD7 /* ShadowsocksCipherPicker.swift in Sources */,
- 58EFC76A2AFAC3B800E9F4CB /* ListAccessMethodHeaderView.swift in Sources */,
58B93A1326C3F13600A55733 /* TunnelState.swift in Sources */,
58FF9FEC2B07A7CB00E4C97D /* NSDirectionalEdgeInsets+Helpers.swift in Sources */,
586C0D832B03D2FF00E7CDD7 /* ShadowsocksSectionHandler.swift in Sources */,
diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodCoordinator.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodCoordinator.swift
index de77f93a43..d219a7bf93 100644
--- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodCoordinator.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodCoordinator.swift
@@ -103,6 +103,24 @@ extension EditAccessMethodCoordinator: EditAccessMethodViewControllerDelegate {
onFinish?(self)
}
+ func controllerShouldShowMethodInfo(_ controller: EditAccessMethodViewController, config: InfoModalConfig) {
+ let aboutController = AboutViewController(
+ header: config.header,
+ preamble: config.preamble,
+ body: config.body
+ )
+ let aboutNavController = UINavigationController(rootViewController: aboutController)
+
+ aboutController.navigationItem.rightBarButtonItem = UIBarButtonItem(
+ systemItem: .done,
+ primaryAction: UIAction { [weak aboutNavController] _ in
+ aboutNavController?.dismiss(animated: true)
+ }
+ )
+
+ navigationController.present(aboutNavController, animated: true)
+ }
+
private func getViewModelSubjectFromStore() -> CurrentValueSubject<AccessMethodViewModel, Never> {
let persistentMethod = accessMethodRepository.fetch(by: methodIdentifier)
return CurrentValueSubject<AccessMethodViewModel, Never>(persistentMethod?.toViewModel() ?? .init())
diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodSectionIdentifier.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodSectionIdentifier.swift
index cd8f978113..da5926a591 100644
--- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodSectionIdentifier.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodSectionIdentifier.swift
@@ -19,14 +19,6 @@ enum EditAccessMethodSectionIdentifier: Hashable {
/// The section footer text.
var sectionFooter: String? {
switch self {
- case .enableMethod:
- NSLocalizedString(
- "ENABLE_METHOD_FOOTER",
- tableName: "APIAccess",
- value: "When enabled, the app can try to communicate with a Mullvad API server using this method.",
- comment: ""
- )
-
case .testMethod:
NSLocalizedString(
"TEST_METHOD_FOOTER",
@@ -35,7 +27,7 @@ enum EditAccessMethodSectionIdentifier: Hashable {
comment: ""
)
- case .methodSettings, .cancelTest, .testingStatus, .deleteMethod:
+ case .enableMethod, .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 f5158ebed5..c3f73d7de9 100644
--- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewController.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewController.swift
@@ -7,15 +7,17 @@
//
import Combine
+import MullvadSettings
import UIKit
/// The view controller providing the interface for editing the existing access method.
-class EditAccessMethodViewController: UITableViewController {
+class EditAccessMethodViewController: UIViewController {
typealias EditAccessMethodDataSource = UITableViewDiffableDataSource<
EditAccessMethodSectionIdentifier,
EditAccessMethodItemIdentifier
>
+ private let tableView = UITableView(frame: .zero, style: .insetGrouped)
private let subject: CurrentValueSubject<AccessMethodViewModel, Never>
private let interactor: EditAccessMethodInteractorProtocol
private var alertPresenter: AlertPresenter
@@ -33,7 +35,7 @@ class EditAccessMethodViewController: UITableViewController {
self.interactor = interactor
self.alertPresenter = alertPresenter
- super.init(style: .insetGrouped)
+ super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
@@ -45,11 +47,19 @@ class EditAccessMethodViewController: UITableViewController {
view.accessibilityIdentifier = .editAccessMethodView
view.backgroundColor = .secondaryColor
+
tableView.backgroundColor = .secondaryColor
- navigationItem.largeTitleDisplayMode = .never
+ tableView.delegate = self
isModalInPresentation = true
+ let headerView = createHeaderView()
+ view.addConstrainedSubviews([headerView, tableView]) {
+ headerView.pinEdgesToSuperviewMargins(PinnableEdges([.leading(8), .trailing(8), .top(0)]))
+ tableView.pinEdgesToSuperview(.all().excluding(.top))
+ tableView.topAnchor.constraint(equalTo: headerView.bottomAnchor, constant: 20)
+ }
+
configureDataSource()
configureNavigationItem()
}
@@ -59,15 +69,32 @@ class EditAccessMethodViewController: UITableViewController {
interactor.cancelProxyConfigurationTest()
}
- // MARK: - UITableViewDelegate
+ private func createHeaderView() -> UIView {
+ var headerView: InfoHeaderView?
+
+ if let headerConfig = subject.value.infoHeaderConfig {
+ headerView = InfoHeaderView(config: headerConfig)
+
+ headerView?.onAbout = { [weak self] in
+ guard let self, let infoModalConfig = subject.value.infoModalConfig else { return }
+ delegate?.controllerShouldShowMethodInfo(self, config: infoModalConfig)
+ }
+ }
+
+ return headerView ?? UIView()
+ }
+}
+
+// MARK: - UITableViewDelegate
- override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
+extension EditAccessMethodViewController: UITableViewDelegate {
+ func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
guard let itemIdentifier = dataSource?.itemIdentifier(for: indexPath) else { return false }
return itemIdentifier.isSelectable
}
- override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let itemIdentifier = dataSource?.itemIdentifier(for: indexPath) else { return }
if case .methodSettings = itemIdentifier {
@@ -75,29 +102,29 @@ class EditAccessMethodViewController: UITableViewController {
}
}
- override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+ func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UIMetrics.SettingsCell.apiAccessCellHeight
}
- override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
+ func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return nil
}
// Header height shenanigans to avoid extra spacing in testing sections when testing is NOT ongoing.
- override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
+ func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
guard let sectionIdentifier = dataSource?.snapshot().sectionIdentifiers[section] else { return 0 }
switch sectionIdentifier {
- case .enableMethod, .methodSettings, .deleteMethod, .testMethod:
+ case .methodSettings, .deleteMethod, .testMethod:
return UITableView.automaticDimension
case .testingStatus:
return subject.value.testingStatus == .initial ? 0 : UITableView.automaticDimension
- case .cancelTest:
+ case .enableMethod, .cancelTest:
return 0
}
}
- override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
+ func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
guard let sectionIdentifier = dataSource?.snapshot().sectionIdentifiers[section] else { return nil }
guard let sectionFooterText = sectionIdentifier.sectionFooter else { return nil }
@@ -114,7 +141,7 @@ class EditAccessMethodViewController: UITableViewController {
}
// Footer height shenanigans to avoid extra spacing in testing sections when testing is NOT ongoing.
- override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
+ func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
guard let sectionIdentifier = dataSource?.snapshot().sectionIdentifiers[section] else { return 0 }
let marginToDeleteMethodItem: CGFloat = 24
@@ -377,4 +404,4 @@ class EditAccessMethodViewController: UITableViewController {
private func onCancelTest() {
interactor.cancelProxyConfigurationTest()
}
-}
+} // swiftlint:disable:this file_length
diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewControllerDelegate.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewControllerDelegate.swift
index 9c0f844786..cd44f48661 100644
--- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewControllerDelegate.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodViewControllerDelegate.swift
@@ -7,6 +7,7 @@
//
import Foundation
+import MullvadSettings
protocol EditAccessMethodViewControllerDelegate: AnyObject, AccessMethodEditing {
/// The view controller requests the delegate to present the proxy configuration view controller.
@@ -19,4 +20,8 @@ protocol EditAccessMethodViewControllerDelegate: AnyObject, AccessMethodEditing
///
/// - Parameter controller: the calling controller.
func controllerDidDeleteAccessMethod(_ controller: EditAccessMethodViewController)
+
+ /// The view controller requests the delegate to present information about the access method.
+ /// - Parameter controller: the calling controller.
+ func controllerShouldShowMethodInfo(_ controller: EditAccessMethodViewController, config: InfoModalConfig)
}
diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodHeaderView.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodHeaderView.swift
deleted file mode 100644
index a65ceaea79..0000000000
--- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodHeaderView.swift
+++ /dev/null
@@ -1,115 +0,0 @@
-//
-// ListAccessMethodHeaderView.swift
-// MullvadVPN
-//
-// Created by pronebird on 07/11/2023.
-// Copyright © 2023 Mullvad VPN AB. All rights reserved.
-//
-
-import UIKit
-
-/// Header view pinned at the top of ``AccessMethodListViewController``.
-class ListAccessMethodHeaderView: UIView, UITextViewDelegate {
- /// Event handler invoked when user taps on the link to learn more about API access.
- var onAbout: (() -> Void)?
-
- private let textView = UITextView()
-
- override init(frame: CGRect) {
- super.init(frame: frame)
-
- textView.backgroundColor = .clear
- textView.dataDetectorTypes = .link
- textView.isSelectable = true
- textView.isEditable = false
- textView.isScrollEnabled = false
- textView.contentInset = .zero
- textView.textContainerInset = .zero
- textView.attributedText = makeAttributedString()
- textView.linkTextAttributes = defaultLinkAttributes
- textView.textContainer.lineFragmentPadding = 0
- textView.delegate = self
-
- directionalLayoutMargins = UIMetrics.contentHeadingLayoutMargins
-
- addSubviews()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- private let defaultTextAttributes: [NSAttributedString.Key: Any] = [
- .font: UIFont.systemFont(ofSize: 13),
- .foregroundColor: UIColor.ContentHeading.textColor,
- ]
-
- private let defaultLinkAttributes: [NSAttributedString.Key: Any] = [
- .font: UIFont.systemFont(ofSize: 13),
- .foregroundColor: UIColor.ContentHeading.linkColor,
- ]
-
- private func makeAttributedString() -> NSAttributedString {
- let body = NSLocalizedString(
- "ACCESS_METHOD_HEADER_BODY",
- tableName: "APIAccess",
- value: "Manage default and setup custom methods to access the Mullvad API.",
- comment: ""
- )
- let link = NSLocalizedString(
- "ACCESS_METHOD_HEADER_LINK",
- tableName: "APIAccess",
- value: "About API access...",
- comment: ""
- )
-
- var linkAttributes = defaultLinkAttributes
- linkAttributes[.link] = "#"
-
- let paragraphStyle = NSMutableParagraphStyle()
- paragraphStyle.lineBreakMode = .byWordWrapping
-
- let attributedString = NSMutableAttributedString()
- attributedString.append(NSAttributedString(string: body, attributes: defaultTextAttributes))
- attributedString.append(NSAttributedString(string: " ", attributes: defaultTextAttributes))
- attributedString.append(NSAttributedString(string: link, attributes: linkAttributes))
- attributedString.addAttribute(
- .paragraphStyle,
- value: paragraphStyle,
- range: NSRange(location: 0, length: attributedString.length)
- )
- return attributedString
- }
-
- private func addSubviews() {
- addConstrainedSubviews([textView]) {
- textView.pinEdgesToSuperviewMargins()
- }
- }
-
- func textView(
- _ textView: UITextView,
- shouldInteractWith URL: URL,
- in characterRange: NSRange,
- interaction: UITextItemInteraction
- ) -> Bool {
- onAbout?()
- return false
- }
-
- @available(iOS 17.0, *)
- func textView(_ textView: UITextView, menuConfigurationFor textItem: UITextItem, defaultMenu: UIMenu) -> UITextItem
- .MenuConfiguration? {
- return nil
- }
-
- @available(iOS 17.0, *)
- func textView(_ textView: UITextView, primaryActionFor textItem: UITextItem, defaultAction: UIAction) -> UIAction? {
- if case .link = textItem.content {
- return UIAction { [weak self] _ in
- self?.onAbout?()
- }
- }
- return nil
- }
-}
diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodViewController.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodViewController.swift
index 474baea1f7..8c0facf98d 100644
--- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodViewController.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodViewController.swift
@@ -26,7 +26,6 @@ class ListAccessMethodViewController: UIViewController, UITableViewDelegate {
ListAccessMethodItemIdentifier
>
- private let headerView = ListAccessMethodHeaderView()
private let interactor: ListAccessMethodInteractorProtocol
private var lastReachableMethodItem: ListAccessMethodItem?
private var cancellables = Set<AnyCancellable>()
@@ -54,10 +53,6 @@ class ListAccessMethodViewController: UIViewController, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
- headerView.onAbout = { [weak self] in
- self?.sendAbout()
- }
-
view.backgroundColor = .secondaryColor
tableView.delegate = self
@@ -69,10 +64,11 @@ class ListAccessMethodViewController: UIViewController, UITableViewDelegate {
view.accessibilityIdentifier = .apiAccessView
+ let headerView = createHeaderView()
view.addConstrainedSubviews([headerView, tableView]) {
- headerView.pinEdgesToSuperview(.all().excluding(.bottom))
+ headerView.pinEdgesToSuperviewMargins(PinnableEdges([.leading(8), .trailing(8), .top(0)]))
tableView.pinEdgesToSuperview(.all().excluding(.top))
- tableView.topAnchor.constraint(equalTo: headerView.bottomAnchor)
+ tableView.topAnchor.constraint(equalTo: headerView.bottomAnchor, constant: 20)
}
addChild(contentController)
@@ -147,6 +143,29 @@ class ListAccessMethodViewController: UIViewController, UITableViewDelegate {
)
}
+ private func createHeaderView() -> InfoHeaderView {
+ let body = NSLocalizedString(
+ "ACCESS_METHOD_HEADER_BODY",
+ tableName: "APIAccess",
+ value: "Manage default and setup custom methods to access the Mullvad API.",
+ comment: ""
+ )
+ let link = NSLocalizedString(
+ "ACCESS_METHOD_HEADER_LINK",
+ tableName: "APIAccess",
+ value: "About API access...",
+ comment: ""
+ )
+
+ let headerView = InfoHeaderView(config: InfoHeaderConfig(body: body, link: link))
+
+ headerView.onAbout = { [weak self] in
+ self?.sendAbout()
+ }
+
+ return headerView
+ }
+
private func configureDataSource() {
dataSource = ListAccessMethodDataSource(
tableView: tableView,
diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift
index 279fa966eb..8adab8c5a9 100644
--- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift
@@ -72,3 +72,166 @@ struct AccessMethodViewModel: Identifiable {
/// Shadowsocks configuration view model.
var shadowsocks = Shadowsocks()
}
+
+extension AccessMethodViewModel {
+ var infoHeaderConfig: InfoHeaderConfig? {
+ switch id {
+ case AccessMethodRepository.directId:
+ InfoHeaderConfig(
+ body: NSLocalizedString(
+ "DIRECT_ACCESS_METHOD_HEADER_BODY",
+ tableName: "APIAccess",
+ value: "The app communicates with a Mullvad API server directly.",
+ comment: ""
+ ),
+ link: NSLocalizedString(
+ "DIRECT_ACCESS_METHOD_HEADER_LINK",
+ tableName: "APIAccess",
+ value: "About Direct method...",
+ comment: ""
+ )
+ )
+ case AccessMethodRepository.bridgeId:
+ InfoHeaderConfig(
+ body: NSLocalizedString(
+ "BRIDGES_ACCESS_METHOD_HEADER_BODY",
+ tableName: "APIAccess",
+ value: "The app communicates with a Mullvad API server via a Mullvad bridge server.",
+ comment: ""
+ ),
+ link: NSLocalizedString(
+ "BRIDGES_ACCESS_METHOD_HEADER_LINK",
+ tableName: "APIAccess",
+ value: "About Mullvad bridges method...",
+ comment: ""
+ )
+ )
+ case AccessMethodRepository.encryptedDNSId:
+ InfoHeaderConfig(
+ body: NSLocalizedString(
+ "ENCRYPTED_DNS_ACCESS_METHOD_HEADER_BODY",
+ tableName: "APIAccess",
+ value: "The app communicates with a Mullvad API server via a proxy address.",
+ comment: ""
+ ),
+ link: NSLocalizedString(
+ "ENCRYPTED_DNS_ACCESS_METHOD_HEADER_LINK",
+ tableName: "APIAccess",
+ value: "About Encrypted DNS proxy method...",
+ comment: ""
+ )
+ )
+ default:
+ nil
+ }
+ }
+
+ var infoModalConfig: InfoModalConfig? {
+ switch id {
+ case AccessMethodRepository.directId:
+ InfoModalConfig(
+ header: NSLocalizedString(
+ "DIRECT_ACCESS_METHOD_MODAL_HEADER",
+ tableName: "APIAccess",
+ value: "Direct",
+ comment: ""
+ ),
+ preamble: NSLocalizedString(
+ "DIRECT_ACCESS_METHOD_MODAL_PREAMBLE",
+ tableName: "APIAccess",
+ value: "The app communicates with a Mullvad API server directly.",
+ comment: ""
+ ),
+ body: [
+ NSLocalizedString(
+ "DIRECT_ACCESS_METHOD_MODAL_BODY_PART_1",
+ tableName: "APIAccess",
+ value: """
+ With the “Direct” method, the app communicates with a Mullvad API server \
+ directly without any intermediate proxies.
+ """,
+ comment: ""
+ ),
+ NSLocalizedString(
+ "DIRECT_ACCESS_METHOD_MODAL_BODY_PART_2",
+ tableName: "APIAccess",
+ value: "This can be useful when you are not affected by censorship.",
+ comment: ""
+ ),
+ ]
+ )
+ case AccessMethodRepository.bridgeId:
+ InfoModalConfig(
+ header: NSLocalizedString(
+ "BRIDGES_ACCESS_METHOD_MODAL_HEADER",
+ tableName: "APIAccess",
+ value: "Mullvad bridges",
+ comment: ""
+ ),
+ preamble: NSLocalizedString(
+ "BRIDGES_ACCESS_METHOD_MODAL_PREAMBLE",
+ tableName: "APIAccess",
+ value: "The app communicates with a Mullvad API server via a Mullvad bridge server.",
+ comment: ""
+ ),
+ body: [
+ NSLocalizedString(
+ "BRIDGES_ACCESS_METHOD_MODAL_BODY_PART_1",
+ tableName: "APIAccess",
+ value: """
+ With the “Mullvad bridges” method, the app communicates with a Mullvad API server via a \
+ Mullvad bridge server. It does this by sending the traffic obfuscated by Shadowsocks.
+ """,
+ comment: ""
+ ),
+ NSLocalizedString(
+ "BRIDGES_ACCESS_METHOD_MODAL_BODY_PART_2",
+ tableName: "APIAccess",
+ value: "This can be useful if the API is censored but Mullvad’s bridge servers are not.",
+ comment: ""
+ ),
+ ]
+ )
+ case AccessMethodRepository.encryptedDNSId:
+ InfoModalConfig(
+ header: NSLocalizedString(
+ "ENCRYPTED_DNS_ACCESS_METHOD_MODAL_HEADER",
+ tableName: "APIAccess",
+ value: "Encrypted DNS proxy",
+ comment: ""
+ ),
+ preamble: NSLocalizedString(
+ "ENCRYPTED_DNS_ACCESS_METHOD_MODAL_PREAMBLE",
+ tableName: "APIAccess",
+ value: "The app communicates with a Mullvad API server via a proxy address.",
+ comment: ""
+ ),
+ body: [
+ NSLocalizedString(
+ "ENCRYPTED_DNS_ACCESS_METHOD_MODAL_BODY_PART_1",
+ tableName: "APIAccess",
+ value: """
+ With the “Encrypted DNS proxy” method, the app will communicate with our \
+ Mullvad API through a proxy address.
+ It does this by retrieving an address from a DNS over HTTPS (DoH) server and \
+ then using that to reach our API servers.
+ """,
+ comment: ""
+ ),
+ NSLocalizedString(
+ "ENCRYPTED_DNS_ACCESS_METHOD_MODAL_BODY_PART_2",
+ tableName: "APIAccess",
+ value: """
+ If you are not connected to our VPN, then the Encrypted DNS proxy will use your own non-VPN IP \
+ when connecting.
+ The DoH servers are hosted by one of the following providers: Quad9, Cloudflare, or Google.
+ """,
+ comment: ""
+ ),
+ ]
+ )
+ default:
+ nil
+ }
+ }
+}
diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift
index be9116823d..8a13306feb 100644
--- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift
@@ -7,13 +7,13 @@
//
import Combine
+import MullvadSettings
import UIKit
class IPOverrideViewController: UIViewController {
private let interactor: IPOverrideInteractor
private var cancellables = Set<AnyCancellable>()
private let alertPresenter: AlertPresenter
- private let headerView = IPOverrideHeaderView()
weak var delegate: IPOverrideViewControllerDelegate?
@@ -81,6 +81,21 @@ class IPOverrideViewController: UIViewController {
}
private func addHeaderView() {
+ let body = NSLocalizedString(
+ "ACCESS_METHOD_HEADER_BODY",
+ tableName: "APIAccess",
+ value: "Manage default and setup custom methods to access the Mullvad API.",
+ comment: ""
+ )
+ let link = NSLocalizedString(
+ "ACCESS_METHOD_HEADER_LINK",
+ tableName: "APIAccess",
+ value: "About API access...",
+ comment: ""
+ )
+
+ let headerView = InfoHeaderView(config: InfoHeaderConfig(body: body, link: link))
+
headerView.onAbout = { [weak self] in
self?.delegate?.presentAbout()
}
diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift
index 5ce99f689f..32facd0eca 100644
--- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift
+++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift
@@ -128,7 +128,6 @@ extension VPNSettingsViewController: VPNSettingsDataSourceDelegate {
interactor.evaluateDaitaSettingsCompatibility(settings)
}
- // swiftlint:disable:next function_body_length
func showPrompt(
for item: VPNSettingsPromptAlertItem,
onSave: @escaping () -> Void,
diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideHeaderView.swift b/ios/MullvadVPN/Views/InfoHeaderView.swift
index 16c751f121..67564bc312 100644
--- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideHeaderView.swift
+++ b/ios/MullvadVPN/Views/InfoHeaderView.swift
@@ -1,22 +1,26 @@
//
-// IPOverrideHeaderView.swift
+// InfoHeaderView.swift
// MullvadVPN
//
-// Created by Jon Petersson on 2024-03-20.
+// Created by Jon Petersson on 2024-10-01.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//
+import MullvadSettings
import UIKit
-/// Header view pinned at the top of ``IPOverrideViewController``.
-class IPOverrideHeaderView: UIView, UITextViewDelegate {
- /// Event handler invoked when user taps on the link to learn more about API access.
+/// Header view pinned at the top of a ``ViewController``.
+class InfoHeaderView: UIView, UITextViewDelegate {
+ /// Event handler invoked when user taps on the link.
var onAbout: (() -> Void)?
private let textView = UITextView()
+ private let config: InfoHeaderConfig
- override init(frame: CGRect) {
- super.init(frame: frame)
+ init(config: InfoHeaderConfig) {
+ self.config = config
+
+ super.init(frame: .zero)
textView.backgroundColor = .clear
textView.dataDetectorTypes = .link
@@ -50,19 +54,6 @@ class IPOverrideHeaderView: UIView, UITextViewDelegate {
]
private func makeAttributedString() -> NSAttributedString {
- let body = NSLocalizedString(
- "IP_OVERRIDE_HEADER_BODY",
- tableName: "IPOverride",
- value: "Import files or text with new IP addresses for the servers in the Select location view.",
- comment: ""
- )
- let link = NSLocalizedString(
- "IP_OVERRIDE_HEADER_LINK",
- tableName: "IPOverride",
- value: "About IP override...",
- comment: ""
- )
-
var linkAttributes = defaultLinkAttributes
linkAttributes[.link] = "#"
@@ -70,9 +61,9 @@ class IPOverrideHeaderView: UIView, UITextViewDelegate {
paragraphStyle.lineBreakMode = .byWordWrapping
let attributedString = NSMutableAttributedString()
- attributedString.append(NSAttributedString(string: body, attributes: defaultTextAttributes))
+ attributedString.append(NSAttributedString(string: config.body, attributes: defaultTextAttributes))
attributedString.append(NSAttributedString(string: " ", attributes: defaultTextAttributes))
- attributedString.append(NSAttributedString(string: link, attributes: linkAttributes))
+ attributedString.append(NSAttributedString(string: config.link, attributes: linkAttributes))
attributedString.addAttribute(
.paragraphStyle,
value: paragraphStyle,