From 514ae14b58056ef490b203db82bbb7901760297a Mon Sep 17 00:00:00 2001 From: Jon Petersson Date: Tue, 12 Nov 2024 09:59:46 +0100 Subject: [PATCH] Add the multihop page to the root of the settings page --- ios/MullvadVPN.xcodeproj/project.pbxproj | 42 +++++++++++ .../Classes/AccessbilityIdentifier.swift | 1 + ios/MullvadVPN/Classes/ViewGeometry.swift | 33 +++++++++ .../Multihop/SettingsMultihopView.swift | 58 +++++++++++++++ .../Settings/SettingsCoordinator.swift | 17 +++++ .../Settings/Views/SettingsInfoView.swift | 39 +++++++++++ .../Settings/Views/SwitchRowView.swift | 57 +++++++++++++++ .../Extensions/View+TapAreaSize.swift | 35 ++++++++++ .../IconAccount.imageset/IconAccount.pdf | Bin 1424 -> 1526 bytes .../IconAlert.imageset/IconAlert.pdf | Bin 1070 -> 1172 bytes .../IconArrow.imageset/IconArrow.pdf | Bin 1105 -> 1209 bytes .../IconBack.imageset/IconBack.pdf | Bin 1125 -> 1228 bytes .../IconChevron.imageset/IconChevron.pdf | Bin 1054 -> 1157 bytes .../IconChevronDown.pdf | Bin 1059 -> 1161 bytes .../IconChevronUp.imageset/IconChevronUp.pdf | Bin 1061 -> 1163 bytes .../IconClose.imageset/IconClose.pdf | Bin 1130 -> 1233 bytes .../IconCloseSml.imageset/IconCloseSml.pdf | Bin 1139 -> 1243 bytes .../IconCopy.imageset/IconCopy.pdf | Bin 1176 -> 1279 bytes .../IconExtlink.imageset/IconExtlink.pdf | Bin 1126 -> 1230 bytes .../IconFail.imageset/IconFail.pdf | Bin 1163 -> 1266 bytes .../IconInfo.imageset/IconInfo.pdf | Bin 1170 -> 1273 bytes .../IconObscure.imageset/IconObscure.pdf | Bin 1483 -> 1585 bytes .../IconReload.imageset/IconReload.pdf | Bin 1236 -> 3272 bytes .../IconSettings.imageset/IconSettings.pdf | Bin 1401 -> 1503 bytes .../IconSpinner.imageset/IconSpinner.pdf | Bin 1313 -> 1414 bytes .../IconSuccess.imageset/IconSuccess.pdf | Bin 1104 -> 1206 bytes .../IconTick.imageset/IconTick.pdf | Bin 1050 -> 1152 bytes .../IconTickSml.imageset/IconTickSml.pdf | Bin 1045 -> 1147 bytes .../IconUnobscure.imageset/IconUnobscure.pdf | Bin 1156 -> 1258 bytes .../LocationMarkerSecure.pdf | Bin 2407 -> 2442 bytes .../LocationMarkerUnsecure.pdf | Bin 2405 -> 2441 bytes .../LogoIcon.imageset/LogoIcon.pdf | Bin 1974 -> 2076 bytes .../LogoText.imageset/LogoText.pdf | Bin 2431 -> 2531 bytes .../Contents.json | 15 ++++ .../MultihopIllustration.pdf | Bin 0 -> 11980 bytes .../UIEdgeInsets+Extensions.swift | 7 +- ios/MullvadVPN/UI appearance/UIMetrics.swift | 19 +++-- .../DeviceList/DeviceRowView.swift | 2 +- .../Settings/SettingsCellFactory.swift | 13 ++++ .../Settings/SettingsDataSource.swift | 29 ++++---- .../Settings/SettingsViewController.swift | 2 + .../VPNSettings/VPNSettingsCellFactory.swift | 23 +----- .../VPNSettings/VPNSettingsDataSource.swift | 17 +---- ios/MullvadVPN/Views/CustomToggleStyle.swift | 66 ++++++++++++++++++ ios/MullvadVPN/Views/IncreasedHitButton.swift | 1 + ios/convert-assets.rb | 1 + 46 files changed, 423 insertions(+), 54 deletions(-) create mode 100644 ios/MullvadVPN/Classes/ViewGeometry.swift create mode 100644 ios/MullvadVPN/Coordinators/Settings/Multihop/SettingsMultihopView.swift create mode 100644 ios/MullvadVPN/Coordinators/Settings/Views/SettingsInfoView.swift create mode 100644 ios/MullvadVPN/Coordinators/Settings/Views/SwitchRowView.swift create mode 100644 ios/MullvadVPN/Extensions/View+TapAreaSize.swift create mode 100644 ios/MullvadVPN/Supporting Files/Assets.xcassets/MultihopIllustration.imageset/Contents.json create mode 100644 ios/MullvadVPN/Supporting Files/Assets.xcassets/MultihopIllustration.imageset/MultihopIllustration.pdf create mode 100644 ios/MullvadVPN/Views/CustomToggleStyle.swift diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index ef1968698bc4..e348035415bb 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -550,6 +550,13 @@ 7A88DCF42A93471F00D2FF0E /* ApplicationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5893C6FB29C311E9009090D1 /* ApplicationRouter.swift */; }; 7A88DCF52A93471F00D2FF0E /* ApplicationRouterTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5802EBCA2A8E45DC00E5CE4C /* ApplicationRouterTypes.swift */; }; 7A88DCF62A93471F00D2FF0E /* AppRouteProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5802EBC62A8E457A00E5CE4C /* AppRouteProtocol.swift */; }; + 7A8A18F92CE34EA8000BCB5B /* SettingsMultihopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A18F82CE34E9F000BCB5B /* SettingsMultihopView.swift */; }; + 7A8A18FB2CE4B678000BCB5B /* View+TapAreaSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A18FA2CE4B66C000BCB5B /* View+TapAreaSize.swift */; }; + 7A8A18FD2CE4BE8D000BCB5B /* CustomToggleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A18FC2CE4BE88000BCB5B /* CustomToggleStyle.swift */; }; + 7A8A19022CE4DD56000BCB5B /* ViewGeometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19012CE4DD4D000BCB5B /* ViewGeometry.swift */; }; + 7A8A19032CE4DD56000BCB5B /* ViewGeometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19012CE4DD4D000BCB5B /* ViewGeometry.swift */; }; + 7A8A19052CE4E9A9000BCB5B /* SwitchRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19042CE4E9A5000BCB5B /* SwitchRowView.swift */; }; + 7A8A19072CE4E9D3000BCB5B /* SettingsInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19062CE4E9CC000BCB5B /* SettingsInfoView.swift */; }; 7A9BE5A22B8F88C500E2A7D0 /* LocationNodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9BE5A12B8F88C500E2A7D0 /* LocationNodeTests.swift */; }; 7A9BE5A32B8F89B900E2A7D0 /* LocationNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6389F72B864CDF008E77E1 /* LocationNode.swift */; }; 7A9BE5A52B90760C00E2A7D0 /* CustomListsDataSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9BE5A42B90760C00E2A7D0 /* CustomListsDataSourceTests.swift */; }; @@ -1880,6 +1887,12 @@ 7A88DCD02A8FABBE00D2FF0E /* Routing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Routing.h; sourceTree = ""; }; 7A88DCD72A8FABBE00D2FF0E /* RoutingTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RoutingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 7A88DCDE2A8FABBF00D2FF0E /* RoutingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutingTests.swift; sourceTree = ""; }; + 7A8A18F82CE34E9F000BCB5B /* SettingsMultihopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsMultihopView.swift; sourceTree = ""; }; + 7A8A18FA2CE4B66C000BCB5B /* View+TapAreaSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+TapAreaSize.swift"; sourceTree = ""; }; + 7A8A18FC2CE4BE88000BCB5B /* CustomToggleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomToggleStyle.swift; sourceTree = ""; }; + 7A8A19012CE4DD4D000BCB5B /* ViewGeometry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewGeometry.swift; sourceTree = ""; }; + 7A8A19042CE4E9A5000BCB5B /* SwitchRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchRowView.swift; sourceTree = ""; }; + 7A8A19062CE4E9CC000BCB5B /* SettingsInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsInfoView.swift; sourceTree = ""; }; 7A9BE5A12B8F88C500E2A7D0 /* LocationNodeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationNodeTests.swift; sourceTree = ""; }; 7A9BE5A42B90760C00E2A7D0 /* CustomListsDataSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListsDataSourceTests.swift; sourceTree = ""; }; 7A9BE5A82B90806800E2A7D0 /* CustomListsRepositoryStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListsRepositoryStub.swift; sourceTree = ""; }; @@ -2920,6 +2933,7 @@ 58ACF64E26567A7100ACE4B7 /* CustomSwitchContainer.swift */, 58293FB025124117005D0BB5 /* CustomTextField.swift */, 58293FB2251241B3005D0BB5 /* CustomTextView.swift */, + 7A8A18FC2CE4BE88000BCB5B /* CustomToggleStyle.swift */, 5892A45D265FABFF00890742 /* EmptyTableViewHeaderFooterView.swift */, 58FD5BF32428C67600112C88 /* InAppPurchaseButton.swift */, F03580242A13842C00E5DAFD /* IncreasedHitButton.swift */, @@ -3003,6 +3017,7 @@ 7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */, 5878F4FF29CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift */, 7A516C2D2B6D357500BBD33D /* URL+Scoping.swift */, + 7A8A18FA2CE4B66C000BCB5B /* View+TapAreaSize.swift */, ); path = Extensions; sourceTree = ""; @@ -3052,6 +3067,7 @@ 582AE30F2440A6CA00E6733A /* InputTextFormatter.swift */, 7A7907322BC0280A00B61F81 /* InterceptibleNavigationController.swift */, 58DFF7D12B0256A300F864E0 /* MarkdownStylingOptions.swift */, + 7A8A19012CE4DD4D000BCB5B /* ViewGeometry.swift */, ); path = Classes; sourceTree = ""; @@ -3737,6 +3753,8 @@ children = ( 58CEB2E72AFBB9F300E6E088 /* APIAccess */, 7A5869A92B55516700640D27 /* IPOverride */, + 7A8A18F72CE34E8F000BCB5B /* Multihop */, + 7A8A18FE2CE4C7FA000BCB5B /* Views */, 58EFC7702AFB45E500E9F4CB /* SettingsChildCoordinator.swift */, 7A9CCCAD2A96302800DD6A34 /* SettingsCoordinator.swift */, 7A6389EC2B7FADA1008E77E1 /* SettingsFieldValidationErrorConfiguration.swift */, @@ -3891,6 +3909,23 @@ path = RoutingTests; sourceTree = ""; }; + 7A8A18F72CE34E8F000BCB5B /* Multihop */ = { + isa = PBXGroup; + children = ( + 7A8A18F82CE34E9F000BCB5B /* SettingsMultihopView.swift */, + ); + path = Multihop; + sourceTree = ""; + }; + 7A8A18FE2CE4C7FA000BCB5B /* Views */ = { + isa = PBXGroup; + children = ( + 7A8A19062CE4E9CC000BCB5B /* SettingsInfoView.swift */, + 7A8A19042CE4E9A5000BCB5B /* SwitchRowView.swift */, + ); + path = Views; + sourceTree = ""; + }; 7A9BE5A02B8F881B00E2A7D0 /* SelectLocation */ = { isa = PBXGroup; children = ( @@ -5430,6 +5465,7 @@ A9B6AC182ADE8F4300F7802A /* MigrationManagerTests.swift in Sources */, 7A9BE5AB2B909A1700E2A7D0 /* LocationDataSourceProtocol.swift in Sources */, A9A5FA2A2ACB05160083449F /* CoordinatesTests.swift in Sources */, + 7A8A19022CE4DD56000BCB5B /* ViewGeometry.swift in Sources */, 44DD7D242B6CFFD70005F67F /* StartTunnelOperationTests.swift in Sources */, 44BB5F982BE527F4002520EB /* TunnelState+UI.swift in Sources */, A9A5FA2B2ACB05160083449F /* CustomDateComponentsFormattingTests.swift in Sources */, @@ -5638,6 +5674,7 @@ 7A9CCCB72A96302800DD6A34 /* RevokedCoordinator.swift in Sources */, 7A6389F82B864CDF008E77E1 /* LocationNode.swift in Sources */, 587D96742886D87C00CD8F1C /* DeviceManagementContentView.swift in Sources */, + 7A8A18F92CE34EA8000BCB5B /* SettingsMultihopView.swift in Sources */, 44BB5F972BE527F4002520EB /* TunnelState+UI.swift in Sources */, 7A11DD0B2A9495D400098CD8 /* AppRoutes.swift in Sources */, 5827B0902B0CAA0500CCBBA1 /* EditAccessMethodCoordinator.swift in Sources */, @@ -5667,8 +5704,10 @@ F062000C2CB7EB5D002E6DB9 /* UIImage+Helpers.swift in Sources */, 7A6389EB2B7FAD7A008E77E1 /* SettingsFieldValidationErrorContentView.swift in Sources */, 58B26E2A2943545A00D5980C /* NotificationManagerDelegate.swift in Sources */, + 7A8A19072CE4E9D3000BCB5B /* SettingsInfoView.swift in Sources */, 58A1AA8C23F5584C009F7EA6 /* ConnectionPanelView.swift in Sources */, 5878A27B2909649A0096FC88 /* CustomOverlayRenderer.swift in Sources */, + 7A8A19052CE4E9A9000BCB5B /* SwitchRowView.swift in Sources */, A91614D62B10B26B00F416EB /* TunnelControlViewModel.swift in Sources */, 7A5869972B32EA4500640D27 /* AppButton.swift in Sources */, 586C0D8F2B03D88100E7CDD7 /* ProxyProtocolConfigurationItemIdentifier.swift in Sources */, @@ -5856,6 +5895,7 @@ 5827B0B02B0F4CCD00CCBBA1 /* ListAccessMethodViewControllerDelegate.swift in Sources */, 588D7EE02AF3A595005DF40A /* ListAccessMethodInteractor.swift in Sources */, 58607A4D2947287800BC467D /* AccountExpiryInAppNotificationProvider.swift in Sources */, + 7A8A18FD2CE4BE8D000BCB5B /* CustomToggleStyle.swift in Sources */, 58C8191829FAA2C400DEB1B4 /* NotificationConfiguration.swift in Sources */, 58FF9FE82B07650A00E4C97D /* ButtonCellContentConfiguration.swift in Sources */, 7A6652B82BB44C3E0042D848 /* LocationDiffableDataSourceProtocol.swift in Sources */, @@ -5936,6 +5976,7 @@ F0E8CC032A4C753B007ED3B4 /* WelcomeViewController.swift in Sources */, 584D26C4270C855B004EA533 /* VPNSettingsDataSource.swift in Sources */, F0D8825B2B04F53600D3EF9A /* OutgoingConnectionData.swift in Sources */, + 7A8A18FB2CE4B678000BCB5B /* View+TapAreaSize.swift in Sources */, 7A6F2FAF2AFE36E7006D0856 /* VPNSettingsInfoButtonItem.swift in Sources */, 7A6389DD2B7E3BD6008E77E1 /* CustomListDataSourceConfiguration.swift in Sources */, 5827B0BF2B14B37D00CCBBA1 /* Publisher+PreviousValue.swift in Sources */, @@ -5946,6 +5987,7 @@ 7A5869A22B502EA800640D27 /* MethodSettingsSectionIdentifier.swift in Sources */, 586C0D812B03CA8400E7CDD7 /* CurrentValueSubject+UIActionBindings.swift in Sources */, 581DFAEA2B176C51005D6D1C /* PersistentProxyConfiguration+ViewModel.swift in Sources */, + 7A8A19032CE4DD56000BCB5B /* ViewGeometry.swift in Sources */, A99E5EE02B7628150033F241 /* ProblemReportViewModel.swift in Sources */, 58FD5BF024238EB300112C88 /* SKProduct+Formatting.swift in Sources */, 58B43C1925F77DB60002C8C3 /* TunnelControlView.swift in Sources */, diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift index c706fd9298cb..41b619b54d40 100644 --- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift +++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift @@ -90,6 +90,7 @@ public enum AccessibilityIdentifier: String { case customListLocationCell case daitaConfirmAlertBackButton case daitaConfirmAlertEnableButton + case multihopCell // Labels case accountPageDeviceNameLabel diff --git a/ios/MullvadVPN/Classes/ViewGeometry.swift b/ios/MullvadVPN/Classes/ViewGeometry.swift new file mode 100644 index 000000000000..bcb8bd4ae2bc --- /dev/null +++ b/ios/MullvadVPN/Classes/ViewGeometry.swift @@ -0,0 +1,33 @@ +// +// ViewGeometry.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-11-13. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +/// Struct for measuring view size. Typically used in a `.background()`, +/// eg. `.background(ViewGeometry { actualViewSize in [...] }). +struct ViewGeometry: View { + let onSizeChange: (CGSize) -> Void + + var body: some View { + GeometryReader { geometry in + Color.clear + .preference(key: ViewSizeKey.self, value: geometry.size) + .onPreferenceChange(ViewSizeKey.self) { + onSizeChange($0) + } + } + } +} + +private struct ViewSizeKey: PreferenceKey { + static var defaultValue: CGSize = .zero + + static func reduce(value: inout CGSize, nextValue: () -> CGSize) { + value = nextValue() + } +} diff --git a/ios/MullvadVPN/Coordinators/Settings/Multihop/SettingsMultihopView.swift b/ios/MullvadVPN/Coordinators/Settings/Multihop/SettingsMultihopView.swift new file mode 100644 index 000000000000..ff770bc57214 --- /dev/null +++ b/ios/MullvadVPN/Coordinators/Settings/Multihop/SettingsMultihopView.swift @@ -0,0 +1,58 @@ +// +// SettingsMultihopView.swift +// MullvadVPN +// +// Created by Andrew Bulhak on 2024-09-23. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +struct SettingsMultihopView: View { + @State private var enabled = true + + var didToggleEnabled: ((Bool) -> Void)? + + var body: some View { + ZStack { + VStack(spacing: 24) { + SettingsInfoView( + viewModel: SettingsInfoViewModel( + body: NSLocalizedString( + "SETTINGS_INFO_MULTIHOP", + tableName: "Settings", + value: """ + Multihop routes your traffic into one WireGuard server and out another, making it \ + harder to trace. This results in increased latency but increases anonymity online. + """, + comment: "" + ), + image: .multihopIllustration + ) + ) + + SwitchRowView( + enabled: enabled, + text: NSLocalizedString( + "SETTINGS_SWITCH_MULTIHOP", + tableName: "Settings", + value: "Enable", + comment: "" + ), + didToggle: { didToggleEnabled?($0) } + ) + + Spacer() + } + .padding(UIMetrics.contentInsets.toEdgeInsets) + } + .background(Color(.secondaryColor)) + .foregroundColor(Color(.primaryTextColor)) + } +} + +#Preview { + SettingsMultihopView { enabled in + print("\(enabled)") + } +} diff --git a/ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift b/ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift index 2e9501f25a36..586e1597c9e0 100644 --- a/ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/Settings/SettingsCoordinator.swift @@ -10,6 +10,7 @@ import MullvadLogging import MullvadSettings import Operations import Routing +import SwiftUI import UIKit /// Settings navigation route. @@ -28,6 +29,9 @@ enum SettingsNavigationRoute: Equatable { /// API access route. case apiAccess + + /// Multihop route. + case multihop } /// Top-level settings coordinator. @@ -267,6 +271,19 @@ final class SettingsCoordinator: Coordinator, Presentable, Presenting, SettingsV case .faq: // Handled separately and presented as a modal. return .failed + + case .multihop: + let view = SettingsMultihopView() + + let host = UIHostingController(rootView: view) + host.title = NSLocalizedString( + "NAVIGATION_TITLE_MULTIHOP", + tableName: "Settings", + value: "Multihop", + comment: "" + ) + + return .viewController(host) } } diff --git a/ios/MullvadVPN/Coordinators/Settings/Views/SettingsInfoView.swift b/ios/MullvadVPN/Coordinators/Settings/Views/SettingsInfoView.swift new file mode 100644 index 000000000000..0d1098f45a0d --- /dev/null +++ b/ios/MullvadVPN/Coordinators/Settings/Views/SettingsInfoView.swift @@ -0,0 +1,39 @@ +// +// SettingsInfoView.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-11-13. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +struct SettingsInfoViewModel { + let body: String + let image: ImageResource +} + +struct SettingsInfoView: View { + let viewModel: SettingsInfoViewModel + + var body: some View { + VStack(alignment: .leading, spacing: 16) { + Image(viewModel.image) + .resizable() + .aspectRatio(contentMode: .fit) + Text(viewModel.body) + .font(.subheadline) + .opacity(0.6) + } + } +} + +#Preview { + SettingsInfoView(viewModel: SettingsInfoViewModel( + body: """ + Multihop routes your traffic into one WireGuard server and out another, making it \ + harder to trace. This results in increased latency but increases anonymity online. + """, + image: .multihopIllustration + )) +} diff --git a/ios/MullvadVPN/Coordinators/Settings/Views/SwitchRowView.swift b/ios/MullvadVPN/Coordinators/Settings/Views/SwitchRowView.swift new file mode 100644 index 000000000000..4d72d332b8d5 --- /dev/null +++ b/ios/MullvadVPN/Coordinators/Settings/Views/SwitchRowView.swift @@ -0,0 +1,57 @@ +// +// SwitchRowView.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-11-13. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +struct SwitchRowView: View { + @State private var enabled: Bool + private let text: String + + let didToggle: (Bool) -> Void + var didTap: (() -> Void)? + + init( + enabled: Bool, + text: String, + didToggle: @escaping ((Bool) -> Void), + didTap: (() -> Void)? = nil + ) { + self.enabled = enabled + self.text = text + self.didToggle = didToggle + self.didTap = didTap + } + + var body: some View { + Toggle(isOn: $enabled, label: { + Text(text) + }).onChange(of: enabled, perform: { enabled in + didToggle(enabled) + }) + .toggleStyle(CustomToggleStyle(infoButtonAction: didTap)) + .font(.headline) + .frame(height: UIMetrics.SettingsViewCell.height) + .padding(UIMetrics.SettingsViewCell.layoutMargins) + .background(Color(.primaryColor)) + .foregroundColor(Color(.primaryTextColor)) + .cornerRadius(UIMetrics.SettingsViewCell.cornerRadius) + .accessibilityIdentifier(AccessibilityIdentifier.multihopSwitch.rawValue) + } +} + +#Preview { + SwitchRowView( + enabled: true, + text: "Enable", + didToggle: { enabled in + print("\(enabled)") + }, didTap: { + print("Tapped") + } + ) +} diff --git a/ios/MullvadVPN/Extensions/View+TapAreaSize.swift b/ios/MullvadVPN/Extensions/View+TapAreaSize.swift new file mode 100644 index 000000000000..4c087f4e44fd --- /dev/null +++ b/ios/MullvadVPN/Extensions/View+TapAreaSize.swift @@ -0,0 +1,35 @@ +// +// View+TapAreaSize.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-11-13. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +extension View { + /// Adjusts tappable area to at least minimum (default) size without changing + /// actual view size. + func adjustingTapAreaSize() -> some View { + modifier(TappablePadding()) + } +} + +private struct TappablePadding: ViewModifier { + @State var actualViewSize: CGSize = .zero + let tappableViewSize = UIMetrics.Button.minimumTappableAreaSize + + func body(content: Content) -> some View { + content + .background(ViewGeometry { + actualViewSize = $0 + }) + .frame( + width: max(actualViewSize.width, tappableViewSize.width), + height: max(actualViewSize.height, tappableViewSize.height) + ) + .contentShape(Rectangle()) + .frame(width: actualViewSize.width, height: actualViewSize.height) + } +} diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconAccount.imageset/IconAccount.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconAccount.imageset/IconAccount.pdf index fd1cda97a78b18729db2900e5bea3782cb6805e1..ef0d4672f4dc6e15b37d77a9e603de444034dd22 100644 GIT binary patch delta 744 zcmbQh{f(PLHNeG9*HF)VBgb{7$yqEy+Wtvd!6ms|3JMDPKB;->B^e493I+;6U;#e` zLon@@SyWu2U@}o%wca?`;6c~3uBS{4Y!-~MT&Z~}#U(|liMb$Feo__}NHd5AnrCPR z6SRa1+Sn-Qhg24%D(GX{YG{OHtqGE~M#fN1PDyIfL?fyC(20)OEd~Ou@Bek}oT9~g zc17xL*KZs2be4)7dAs(Dz@rCjT@zKNz1#oZ<+`q_qJez=r+X?KN=}UZN~aiqy0j!U z2ZlO+tCQs1+Q3?W3%#tQWV32qM2tr3f5Av ztG3_uTxWJ>{?m$Ur|Op;`{ubRCfTzpx2^dLOQBwoqS;TejW?$KP&i`yKIQ8VZ`SfR zGPk5NHIHx{O#9$AcUtx5!_wZEv+Ej>HYvjLLYDLpm5$@*VY{o$+imrm#DLO#6;hGhb;?ItAnu?VILl=dhz&7AnuJfJ7Eqmhw; iiHXe->P-ct)K*-QSX5F`1oVQTk);Kfs;aBM8y5g%lnDR; delta 712 zcmZXSO>V+45QU|#P`JcwqJpXtJCFbsVgVF{s;Ve-S9ReA1F29P*-i!BaD`r_r|C_) z?$|W^Mo}bxewjCK?BDWNx#Ug;!&_PlzDqw>TOSCBw|A%00q=Q}aquQ=!NCRU0GNg3 zsRoLw$bv^cOIS~=;RO)^zhT~2yVVz|MzMm%UOkc`&!E#W`=;E^!O|Vtf;UAhS*kK) z6>kdZ7_`iJvFf90z(10hN28vB-LCn15mzFYF;`HxrighuGl5LYm%)_%>mivb41n3U z5be9qO;G1hAv~%ukz$s|TtX#gi4;IdX3 zAPj1pM7!+?w!L($_ic5Rf prpYJAoXT5lX|@Sy8i*Hb12HVejBuGGAg;*z4 R<7KPig~q!~m5%`-HE z30gu0ZEO_uLn;eW74$J}H8eu9)&$8~BV#BhrzEv#qLEa6=tRft76XCS_y4+fPSIjL zyCQYB>$eShI!i^4yj}Z6;L!uNu8Ats-tB+ya$Q$d(Lg@`(>)aqB`3yyrBjSQU0RZw z14A9Z)k$)0a{Kl~iQzGKE7MFJNNJ@3gf=9M$J_4Zlcu~~URDT-l1(abX*1#2nT zRom}+t}{C`|7pdwQ}s)aee>KDlk8cQ+t&PrrBJU((d?(##v9XqC>*hUpYrvGH*5JD znOoACnnySerhRamJFWWj;Y`-`dw8mSZ7Rg9HS%9AHoeRdD%usG&u1Kav2*3rX6fe1 zyVm?*-7`01ZC04P7K;R}s?Q%j`KR?v6LNi0cqNlngA zNd=1sXI9mxDi}iICtM-gP{BySC>AUgl%HRsU}y-BMbEsnd<8=TFyGFO3z|$S<|HR1 z{P;h=vvG1Gqlr84o$s7{U;ngP?b-P84%_~3Yjah*fPj5`T5$}{>#n5W%t%I|4cG$E8%_%e zSVbiUJkv!^hHMLO3L$WNdcB*Y5i6t$7SKCHPdKYeh+}=O(=`f)HmC+?t}rKsD793& z+lNj;@`~?k-WT@d_p#M%n1*>>U1$ahOVOlJ-AzkvNF=1{!a`4?3+ k`(}uYNzm4tCpImX40Q7o-5DVXm&Rl0MpnE1@bYN=0j7B^e493I+;6U;#e` zLon@@SyWu2U@}o%wca?`;6c~3uBS{4Y!-~MT&Z~}#U(|liMb$Feo__}NHd5AnrCPR z6SRa1+Sn-Qhg24%D(GX{YG{OHtqGE~M#fN1PDyIfL?fyC(20TGhYdtp@Bh|5*S$k& z%g(nYey8NF9TX7s+HU?)F=e5)NARSOJNv&ci&dW_utWa&vpKT`TwIv?l}LY=pNXjbY=2kkMxp+tkmbf z9xTx_u4~=iJa6@+Q0u>UwPN3IXDoVjAp7l^gsEjG^5?PVh17Aq`@#K>w?5oUf4@)h z+}`>Bt)#l^7!S!4MKd;R?})3PuV>v0$m7{QMFHLqm8xdgi6&D;OGp`F3_((1cPk zCpjVE$N%}AjguQ0T?`D3+>8tij6N`Px}{H$upVZ^1X>gw;t1pwfe42l2% delta 711 zcmZXSOHRWu5QfFB9=y5TZ)iAr>Za+n_3rYzMV$at@Bcl4Ecq zY?nSzys+fYG@%QCVt7Xr}lN;=WU#-uJ+5-ga_01X#z++PiZn8X*e9}-A$-q6;G)E--s$qi%c8RB{z);^Xfj&gfgJK z*N_~mue4F?lp#9HFy}0*Qp%y7lAJTZ4vriI?X{GpxOH7aSDcjVJQWV(t4_x##henE zvtmpng|>%0gt}fIW4|BZYlN<9ggR%E-80F3uVNy}hOJC8JsI&gi&L3fPL~#fo`sGJ zqA>JBSi*rtzG{zNP#jr+m73E32mK&y1R~sIzSm3`^?QxCJ*=91(2$(u8_LaJTjU>9 l)f}23GA2P=-+!@Lv1Fi|pXkmANw_o~dr@H7_TA&e`US^buZsWx diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconBack.imageset/IconBack.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconBack.imageset/IconBack.pdf index 30613fa0b852865fb8e15da994577bbb69829195..60a1ce78cfb21d8e404b8f4718e56e764ee0e4aa 100644 GIT binary patch delta 758 zcmaFLafXvaHNeG9*HF)VBga}sTXO{ih5V!}E*l#K{gBFnR0Vziq^#hQTrLF#1%02? zy!4U`1q+~>Ah3X+f+3i8%PcA`Q81aPu3B##Z1AA#S=Uo01~v=ESgzE(l;V=2)Wlp6 z3uG`zGl&M7XJ`f!v?Rh-Ln9<>O^~cLGKO+;N>Ym^8cEfMPISy}F%W2d|F3K36fM@X zD^hp6e%qj@vsC2B+qGW=9z9^|ny51E-TwD3*L77D4dnAb-BaOEa$@XPI>q?Yr6s93 zFx2r|oh0Wbw{K6B7*52MK7YVmByi!-^PVhYUO9tXZ=dxYo0S)oq8Jtw%{=2#u$F>d zwf(N=IP|IJ`o_(0dvIMS7`YxeU_>8bHe*8iI74_~dnbXxy%eEQ?( z`}>Y9ir*!f$AlRR;J}1NgORy0EFKJvF(M)&C^ZcnzR<`wwL}VS1%0=i#FA8()a3k> zRIqq(W>tNvf*~Y+!WE(o6^s;&V!={D`S~RZhKBH1^vp}kS1>dH^X=@opvk0SPI5xR zkN@*K8z(n1x)>N5Ju@;eF#5pEd8}Z9gmuSOQ4fQe%^dO_R&D|Z&zz_c)HwF1sgaT4 i$z?WSs5cdmQd@CJVo^y&5zq^U1_lOPs;aL3Zd?G5Z4WL0 delta 687 zcmZXS%T59@6oy0G;Ot$RoDC6TWZJng7{US&LX43N?u?5%6c|IMleS~P%{+u}VdBE~ z@HKo7+j0|WCX@8*e=h%NzfXV4<;tknzruC@tNd9i96-RHU#wOO?1xoG!5)zrg$iH` zKyNrrC19nB47j6{gml;v9u-31c=~%XM_ramB^KcA)C11)3|cLHuG6h67`jFU*kgq` zNkyhh#jCA!2$JV?yXz`dkG>``jRqYJ+im@~BPv+VV=BNkMHQy$L>tmA9~%=U>Nc4O zWk7i^Alh}GYopdaL+~fVh_gu^Qx4UbB%A@Rt2g9^Fcal!@;4}+C1bk#8R(uE}_$%1n8 rrpCz|RXv-#VoZYSW%H7T>6C$XT5lX|@Sy8i*Hb12HVejBuGGAg;*z4 R<7KPig~q!~m5%`-HE z30gu0ZEO_uLn;eW74$J}H8eu9)&$8~BV#BhrzEv#qLEa6=)}P6W&@Gd`+r5Br}{`{ zERtR4{40PvwWo1QwRAOy;f(_Zla6fs_xo-}X-~_6zZT}t@3b_#&UrY|)zV__oW&ZE zA)zPkt#5X^*5S94!{OVi=yv`m2^J5FowiC=^UJ+03wBTTwf0IC^j^4@(f?=gDS`Ms z@^^}-`EJ>qv?JqXP<`@lm6_`|dR659YW~1gc&_N!shZQ38}EK#KlUzDz595))}4vP z8Tl=tif$ZcjpkRgHk#W{nRZd@;Ahi<_nxmeL>_g|KE@a-+7)n~O*r<#%oU=0rJE-1 z(fh%AuPwK?bBYB++p`KD%D`Wi+ZW6KFpt))-}A%hZmvny z-`goFzgWXra?fMN0yr?C(O_h542uUtV~mK12ue)@hc7hpO)Zf^TS4C~C$S{eB{exe zB^4|loLN<$s$d9-pKyg}Lj@xRqgb$1P=0=if}tTi7CrOQ@)ZmXzua6al?pX>7oys_N?R#svTowhDUy delta 710 zcmZXS-%i3X6vpEn?kUa<5n^QR#x^j73qT1mMl!rJUer;>3|W`9GvEzZzJ~AM8+uw8 zp!8x%e?9xY^Y82OyH=}DNB1|h75voFi>(I)#M`^m>w){cDLA-OmU5^AbpXso@>~PO zsMvx>KF`=dtleh8FO(iRqS)y7(rp=~x8cNP|Ch`%} z92y?+2#LHdrGB?fuL-##ThctSj7}_fy&5vjRwA`xW-w|$ies8<$rlcRjzbOy!XWSi zSU|;s4%TW8nbfw%4)vXS{qE`B`3079uEYQU diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconChevronDown.imageset/IconChevronDown.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconChevronDown.imageset/IconChevronDown.pdf index f529e1990888e1ab36274cb0614ac1fefc2737ce..41bc16dfbd9312061fe6d1bc431a7a88c00b915d 100644 GIT binary patch delta 757 zcmZ3?(aFi78sOrlYp7?wkt3VY)?C3rAwMaL%f?1QKcuoCRYBiBDJ!@nmrFrGLEk4e zFTEr~!2+lz2rS^IU&IlGLJ!MpE^m6CJZ#3tmSWH zZb@fq9^p8c_Q7rLwCc}?Gg;T~;i>kusSvZ)$bYri^fE`NXjgzfpK}dTM-=^}nY2!&mDsoz}k`pZ@sy z{=Q?2;&(~rF=566I545nU}SC#iw8qvjEIN`N=*ZYFEsK^Es;W7LEkMWu_Ve z6)YZ{Syi8^UjLJ@eA?6$}l)d^tJ0E(V51S;htiMjx0t-2x{_Sa*c-DD7k7RMYFQauYas=ENO-k>t2z2N)R6 hSFpN3y(y2B+KNjOi%KerfL^dPHsMlLb@g}S0sxiS3()`o delta 687 zcmZXSK~KUk6vson;O-tx-enPDWbI%Z7{UP%LX43NZ^lC%1*T+O+RlKNojv;z`~-eO z+rkFKW?9nT`}e)~f9==#Pqk`Khoc+Z@P4cBmD~jc?Dfs+cENetrWBkhSyHe8TL5~) zX(9nDRcOEioyVllHt?(v0=K2V7ZVz=L@Kd>R;eCymZi|`>bOp~t6=C3<>1T|<|GlR zE){JH=>#OtXt5h8)sTL~5e>(E4SPNPcS0&y#v>}AX^JXLlespeTmElMm{a%hTqpy| zdk$gQeWs0ChYbEvhAC(BETSB05s5hi+`xVVL2V^vDsCOe&?P77Dvm^h@nyYkDh)U# zGG@t;ND4I&IlGLJ!MpE^m6CJZ#3tmSWH zZb@fq9^p8c_Q7rLwCc}?Gg;T~;i>kusSvZ)$bYri^fE`NXjgzfpK}dTM-=^}nY2!&mDsoz}k`pZ@sy z{=Q?2;&(~rF=566I545nU}SC#iw8qvjEIN`N=*ZYFEsK^Es;W7LEkMWu_Ve z6)YZ{Syi8^UjLJ@eA?6$}l)d^tJ0E(V51uZ#=~j6N`Px)n~4upY{sRk>gw;t1pwT44529GP7{UP%LX43VZ^pygE>MEqW_N4AOCP|a&)@_2 zhVCv>Fl?G8^PAb3`Ooa<`FFKin+``exas~>-ztRz2-xe&>UP0?+GZ5&DOpgc0k#0N z!D%W1tEkX`2O1}&&o=O^5CW&IzZc^guvDsG0qrt+%vqj6x2yd+-L8V6J5+!@Q<#%f zWV%$e-G@#<@|^Cifr=W^_avg>xUXTar~h_H1%1qqawxJ77vSkv9CgY1n!&Dk@ zN@T*)A(0g74ssCkoesvWP8(k%bajSM<7-v9ECxi9HCvcVdIREb=7)ARoX#x-frZKg zp6j+;n8TjAK?jT%(vr1&H5z@68B*gpW`#De`yVRg_@@ayS1nkdqlCT}9GK)JSyOJ_ p)GYa+s%Nt+#w7UV+nA(t271P!n)!t#B;nF%3>;+DYIlz#>lebxs9692 diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconClose.imageset/IconClose.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconClose.imageset/IconClose.pdf index fcff861b53cf9ee14abea298faf1a1af003f4431..f0d6ffa748bce85dc49c89865d076f5619fe9251 100644 GIT binary patch delta 744 zcmaFGagmclHNeG9*HF)VBgZDj$*#;o+Wtvd!6ms|3JMDPKB;->B^e493I+;6U;#e` zLon@@SyWu2U@}o%wca?`;6c~3uBS{4Y!-~MT&Z~}#U(|liMb$Feo__}NHd5AnrCPR z6SRa1+Sn-Qhg24%D(GX{YG{OHtqGE~M#fN1PDyIfL?fyC(20)OEd~Ou@Bek}oT9~g zc17xL*KZs2be4)7dAs(Dz@rCjT@zKNz1#oZ<+`q_qJez=r+X?KN=}UZN~aiqy0j!U z2ZlO+tCQs1+Q3?W3%#tQWV32qM2tr3f5Av ztG3_uTxWJ>{?m$Ur|Op;`{ubRCfTzpx2^dLOQBwoqS;TejW?$KP&i`yKIQ8VZ`SfR zGPk5NHIHx{O#9$AcUtx5!_wZEv+Ej>HYvjLLYDLpm5$@*VY{o$+imrm(k}g}qlR^j_Pk+zG)nl1d!2-Np^pLY6hjv^0b-Hy0L)WMTd!jHW znaFjic(n~3f#e0)O1=HyDbl2A#{0)Q2pcc@(x+44ebAi3c22K!lt8UtAh3X+f+3i8%PcA`Q81aPu3B##Z1AA#S=Uo01~v=ESgzE(l;V=2)Wlp6 z3uG`zGl&M7XJ`f!v?Rh-Ln9<>O^~cLGKO+;N>Ym^8cEfMPP~}iY#?y#{$K5PsXme! ze!EL$FSSQ$B=a2kmitEW$YHj&i7I_x_uqHTKJLMBr~mz@+UW`%9IQ578thMm1-ne$ zQaNwFG!jVJ`sRcy!-TyruN`E6qOhRxJZEUy8{32}cl~6yn!a6e!fDNtZEOZdpNBZq ze5)(ak2Je@?$pCIQ|rBZ-%d)|cWly^TVJYQaz2WA;udhs^@t_e?(eSYPzc z#^Q|iOP2~Lv&=qVy*29H8UE#>dp>BsQ7wEQ`6i(>SbmKpyT<8&ma4KDFPi#%*1owb zFy~6@!SxT`dU%I!%<{h<6>*^=P)y;fo zC2jw|)I{tr)9w{{ESS*%4ozr07@3>EBErxZBPJq(Qq#Zz42^wLOQhgd(09v8EJ<}q zP0mkA1&aq~)>ow}7(yZ_Tp`*}!AQX<7AzH%pI@S2Xb6u+&%CsJ1w#Wc-_DK;nocU_ zBqt>N_&>k1adIQ0i-Dn0n6ZI@(FbPEV<8hHtUF32JPdX=b!c>0xd|LRbK(z!#<4$h m8yOki2e6exys3zs+=@#Qi%KerfL<^(Ff`>-Rdw}u;{pI>#u+^T delta 687 zcmZXS-)h1z6vmO=xZdw_aBj+!F`HQ1>JTr~DKf^S?r!WNYom5ZQ<8M(Zu$^=lHCpV zD0_^(z>@eMLn-8UzMONull-3lmCN?HKe)j)@2mV%D(ZlMy}nuPHaJhKjDj;JGYU3f z3qWr;O(kHZ3JrLmaYDLm3C{{4z`p)oOsL0Fsl);rJN1yWJcD*y$91}O1w+@U0B52w zC#lGEsc5y8jzIF9Zg)MU>eKfmqT#TsVW*@2PDlmIc|--crl`U+jkO`&@~JUlPTeQ5 zPzIFu0>WMQxi)I;Gx!G?#+=1@L^)ITybB!qPsG6e@L8M<}SbFm_uFe2vi68A8?1@6u(_Ba$rG%pB4;AT~4Kx3lDQY9VM^ zXt%)kJlBIMY?e<{CV-i%l%}W}kQwDm*p_=)HB_!d}cx<+smTliX4y=EZXQ`wB diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconCopy.imageset/IconCopy.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconCopy.imageset/IconCopy.pdf index 45cc203c2335ddaff7d7577daccdeff2573f5987..7fdfe19b2b96f0ed0a01ac78c393398e9a3609e0 100644 GIT binary patch delta 744 zcmbQi`JaB^e493I+;6U;#e` zLon@@SyWu2U@}o%wca?`;6c~3uBS{4Y!-~MT&Z~}#U(|liMb$Feo__}NHd5AnrCPR z6SRa1+Sn-Qhg24%D(GX{YG{OHtqGE~M#fN1PDyIfL?fyC(20)OEd~Ou@Bek}oT9~g zc17xL*KZs2be4)7dAs(Dz@rCjT@zKNz1#oZ<+`q_qJez=r+X?KN=}UZN~aiqy0j!U z2ZlO+tCQs1+Q3?W3%#tQWV32qM2tr3f5Av ztG3_uTxWJ>{?m$Ur|Op;`{ubRCfTzpx2^dLOQBwoqS;TejW?$KP&i`yKIQ8VZ`SfR zGPk5NHIHx{O#9$AcUtx5!_wZEv+Ej>HYvjLLYDLpm5$@*VY{o$+imrm#DLOrnxhGhb;Stlf%ViNo+P}-x=G;?ArtHv>ua6al?pXkco{rK;-c@5TiHG0h9R delta 687 zcmZXSK~KUk6vson!0sMR-enPDWbI&M7{UP%LX43NZ^lF23e1poX*&a6_949ZHT*y( zeiLnr3=Pe)+jKQ4OuFcSU|f}PdLjn==JozPPeOI=nfU&%oXM& z6`3v-ZTHeCNS@PuG*qe)eMur3P6itG`}*&MRIr>!R6xrVRhXu+Hl$lVHYUuen zfbw2ISazRlqt+pV|0lzYvpA0^hk8U3&Hy*D-$YPfNtuZ&$1!xtNw!KN(PVttXqZYv zPKiudIwF!n-9;`!9k+{dtJ}sG2%VoG)Oh=>oE1YN$(k+AA$Xj1c8Oh z1-|FCJXpYAh3X+f+3i8%PcA`Q81aPu3B##Z1AA#S=Uo01~v=ESgzE(l;V=2)Wlp6 z3uG`zGl&M7XJ`f!v?Rh-Ln9<>O^~cLGKO+;N>Ym^8cEfMPP~}iY#?y#{$K5PsXme! ze!EL$FSSQ$B=a2kmitEW$YHj&i7I_x_uqHTKJLMBr~mz@+UW`%9IQ578thMm1-ne$ zQaNwFG!jVJ`sRcy!-TyruN`E6qOhRxJZEUy8{32}cl~6yn!a6e!fDNtZEOZdpNBZq ze5)(ak2Je@?$pCIQ|rBZ-%d)|cWly^TVJYQaz2WA;udhs^@t_e?(eSYPzc z#^Q|iOP2~Lv&=qVy*29H8UE#>dp>BsQ7wEQ`6i(>SbmKpyT<8&ma4KDFPi#%*1owb zFy~6@!SxT`dU%I!%<{h<6>*^=P)y;fo zC2jw|)I{tr)9w{{ESS*%4ozr07@3>EBErxZBPJq(Qq#Zz42^wLOQhgd(09v8EJ<}q zP0mkA1&aq~)>ow}7(yZ_Tp`*}!AQX<7AzH%pI@S2Xb6u+&%CsJ1w#Wc-_DK;nocU_ zBqt>N_&>k1adIQ0i-DohGeZLdqYuoS$2cZPSa)pY^)QHR;E3hHz{0MZB;OaK4? delta 687 zcmZXS(N4lJ6ox~*!QFdla&Cwa87RJ_?q#~^u5cTr!d2J|(FX*B9-*zM}S6H&o(9#a9XDXK6{XWEc%`Pi5+r*4y( zPzIFu0;00}TpP9a8T^9`6V7INOgYqJl5hst#=ec9zLYW(*N$W8f|G2S#KLBL(P)@T zeNKr?SUMn*LcNKa2(_CXjNMKPUm|pIf>7iAqjFyKi6kqwFo*OFh`*Wd+gWouw-5vt zDi`>k=Xx-Q9rHakd%YknS<4NMnCmNZhq^)s*!vF^a$Cm<+qiS23tTmSy>wy8NwT8c rys2sOMpe(|t{9WhF5kvDoiorgj?~Q0EFlS(#$yn8R;_k(KeT=U8Oo_< diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconFail.imageset/IconFail.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconFail.imageset/IconFail.pdf index e36941700ad6e685946993d633f90aab6d923d25..e8c05c2566ec98b2cc6348a4107e6e4e7b65cce7 100644 GIT binary patch delta 743 zcmeC?{KUzj8sOrlYp7?wk>fJsZU3aK;F4S}1qB6tpVYkck_-h41p|d3uz;U} zA((c{EGjNhFqx>XT5lX|@Sy8i*Hb12HVejBuGGAg;*z4 R<7KPig~q!~m5%`-HE z30gu0ZEO_uLn;eW74$J}H8eu9)&$8~BV#BhrzEv#qLFm{#EaR@1_HLZ!q zx4TsKQhSs}GS883xo;GY9OgRgA$0uL{r5uI$2~ai^uPa9J6)lJgVm->gZ+uHV3(;| zD(B6YMnWlD-<)t`n6UTj+E)G(jtUAZR@-BqAVyyA#9Vml)=&GAr3WV zd0X@&#WK>LRJ@z&Tfgej*Qod6#FY~V`|f$h ziv8g_=d&e5boI9{ceS(ltobcBKIe_Jm2?&Bn*DsD^winS(f^w24~Jf=KdFB=HvRGQ z{e8z4#qUzgW5SFCa9BcP!N}Yg77d2R81WDhl$r((UTEB#S|SCug1%c$Vo9n?YI1%` zDp))?vnsV-!4MKX;R?})3PuV>v0$m7{QMFHLqm8Zdgi6&D;OGp`F3_(&{R?}CpjVE z$N%}AjguQ0T?`D3{L&HA;w6CH{+ph1!l;)w4DJj`v4yOBz_YU zzloo~wzxq;vn=he|GxMBul=ok)T;IQcyf(f?pO7vk~@Hay}VleKG=`Dl!84cD++bM z7Jy!Gnn=J(6&P?&qnHfY7M>JB;F<5)Y>ikVl~_QhR8Kj}Qt0>fz7Ds~VCWv@U@sKr zBoV1j748b@3?$EJu^K7Wn7+j!4W>g42Lt`L11ea?Ln@$ck}6D-NE^~6pBfY9)Lk42 zWk7k)A%LR#OdGWh5xk=abIzhHq#PO{i8%w@!d?qOV=ZMWZfx7o6({LB4n>RcRkLX_ zjW{JTX33aH3JnK22>DJA<94ruuMoOCL#X-rUAf3dM3N0#nM3*p#DC8ZvFxor^0skG@$RQ&Kw_B^RBO6L%}2`@rH8q qrWWx#RW+NtVoZWxzKvP3WT1N-sGgr&LJ}^G$DZR@_4@6@#QFma1*vTS diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconInfo.imageset/IconInfo.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconInfo.imageset/IconInfo.pdf index 07f05b149b6024613320d42582eea5b87ccc5a5c..daa96b4af351eb6f514c1e3e75553bb4f2a06ea6 100644 GIT binary patch delta 756 zcmbQl`ID1FHNeG9*HF)VBgZX9TXO{ih5V!}E*l#K{gBFnR0Vziq^#hQTrLF#1%02? zy!4U`1q+~>Ah3X+f+3i8%PcA`Q81aPu3B##Z1AA#S=Uo01~v=ESgzE(l;V=2)Wlp6 z3uG`zGl&M7XJ`f!v?Rh-Ln9<>O^~cLGKO+;N>Ym^8p+gO%x*RiIClT9_PbOc$qYa1 zTYN8_b*C7zCH*%0Ch#bc`LKu3@n84fFWQ={!t(BT`Okgc0xm5~^IWGef9m8oDz$i1 z%bOZmj!jWzPm~x=+$(wgfVoKE!hz>4TE^ex3~s$$)^}@GSxAavSWqCk zcBbYLj)Q6+{MJsZ{(Lx-RsT-QUX^#Y-Lh-my;^Mgm?2fNE8skzaO{Q7l~eahH%;E9 z_d|4#Z01U>FxeN;+F5iPb$+w;zoz=bp_fkA>tDW`_W1e! z-eZg6_npe)z>EWMP(mZY$lMqf3x>uR(GU@ong$MCXw;ipB89brzFSUWNvca~a(+rG zSUfnhDpjH05E47#3ekoNMhZr;V5y+|{1OF2LwFo|=B4E;7#e{2c6MCQL{c#)IU(W4 z|M{JblN%Xb3=ECVB_$-JJYW~<(N!=k6L>8+A;F7LaH>FQk3!STi9h87dR(V8GBPk% hviZZkDF;t$#U+VFB^5-^xd&Y>)ecYh3rf%0H#T1qAHn)oQoFd0b}{oH3bGumM{D z+Tb*mfK^mvz&)KNq{~+Dq!0oe=)FUpxxGfoo-XX&Fs>01)hXxP=T)6st?qJrf-rUDwKsKPXzYC*c?Q(?l~x=W@) z2~gS#i1yv*TBvo%;2&ieb2iOm%Ap#QgfqZ(?AH-g7gA>8#&HatbCNBRSkxJx*J`Fx zk5eKOmiCFHP<4@uP|FQ5ZiG#Ih0x_0LbcED(nZlDk}TQWT+$m5+nFEQS#dhE5Cj(5 zAMib|;lT`c%nt)FUP$+>87Q6l95bwzcgzZHVDCRvs1ckdZ24-|`Wz(;TCD?B^e493I+;6U;#e` zLon@@SyWu2U@}o%wca?`;6c~3uBS{4Y!-~MT&Z~}#U(|liMb$Feo__}NHd5AnrCPR z6SRa1+Sn-Qhg24%D(GX{YG{OHtqGE~M#fN1PDyIfL?fyC(20)OEd~Ou@Bek}oT9~g zc17xL*KZs2be4)7dAs(Dz@rCjT@zKNz1#oZ<+`q_qJez=r+X?KN=}UZN~aiqy0j!U z2ZlO+tCQs1+Q3?W3%#tQWV32qM2tr3f5Av ztG3_uTxWJ>{?m$Ur|Op;`{ubRCfTzpx2^dLOQBwoqS;TejW?$KP&i`yKIQ8VZ`SfR zGPk5NHIHx{O#9$AcUtx5!_wZEv+Ej>HYvjLLYDLpm5$@*VY{o$+imrm#DLO#_FNpBp)Hj8x3U{*47x?(a=yg*{LJ~J}| is}bu1s5cdmQd@CJVo^y&5zq^U#-`?6s;aL3Zd?GglL!m| delta 688 zcmZXS&uhXk6vq!TXfG0XhNS7_)RUrhfr@uF|6|z(+u>fzc9&nat&~EE}oo-XX&L6eILYQoENYC;s#Q}d zn#u8B6L$isQP_Tx-LQ@$%4(yAw2=Hz4JpmOHQX2f~JM` z3mU#(_hAZKh7ndn(F@WYLqSW;<3Dqi_hVM*0Q-NTLV-%9w~i8G6rAWVZnh3O$w{)H s+`OrA@$Z6|B>#gD2wPLA6z?P^?93ttYLDT5p<)N2`7BZ8oBW);j&OnK#*eZ+GAKeZTMb z{l4dupo;XP{R0V~UAMM8CX|2zru5lFa4;a%oH0w71;)XpBmw|HM(Pa$$HS|E6*v{A zGi7kZ#EFDi;5jyjC|IXRretAh;egGkE?L)-B46-mkt=ciF!5+$2jTy4#{3Yutg=qd z5}Yx^_6@v4){qHwg+`P#dWlGi zH1RojB`nNT08-&r6kwESq~YZx&TPu#b(|TD$7Y}_cKCcRx5bkI3DIQ7nB?PRfYN1$ zm({oLGOrn(1DzY38)VBi$lvaX9ro7-o+3yJ4DBukhRr$xFUu^V7ZwZ1IOsNO0Hh|C zHD{x#5eA50^`}H&;Z+0`#c!r*0EZ1Gab1E#1e<0696jKspXJmMiZ;S2DeQA^==X}X z5{OLDBhHp+l?(^+VCnefFjXAH+a@)&SiPds4J9~V}QWtandXtzNO;w^*Syo zDhw^!BDUue(g!V*|1HY@hTOCg!0`<4=V`?-iKsBCexp{J8>|ERd9^JW2$>ZNnT2>& zeqTP)0k2U0q}u4ocw4TRARPJsxq5jaqQ?O^ddpQ?yb>zFd_bq`=d=|Q7vxICikqt|cv4@va= zbLqncnJXLp&qTcYw8}bDSi4$2#XY#BO4wT&u_Y_%=dKTq-kMaJdxV%a*>r~Sk<7wwKU!&G&Y+dE+!NgL8;pL>mwXD4K*S?8D0r_0>c?q%PMrFUOV853(7p3r9>S*P0J*IaMd`4+kFRIzdM z!ZY!;KRE9S`Oa&3&eUJ#8Dis>+%D_$)8i9`li$oyKyr*hU0x!#Sf&miSLV%j%0)-K=$rf2e8psM_y+-dx^Vx04UdbMt(y~1kYE31lsia`bSOu>4YZ36Bb}Pj;ngOgNn9p| zT+&|ZAC6M`c^oVS{K*uMN&^NkwsWe6B^|0=W56?w_G&GL1#bH=>pDYs;?ec27I4i zaqG48jM;aBH40uZF6?~o`GGrdS8d;V!~XZb=gx~)-}|4Z_o$qk>->~ebGnSXRU$RR zxo2*kH933JLNC3zrzOhq$ATx<6s?ylQTI=&n#dNeakOO9QNh~2iFfoo?zC-DN}ON9 zmc5|v_Q8dlPGny?CVk`alHAvQe1EUpp4KTV^|CU;H{0B4QsFNtk8>wA3na7B-!WbD z;F|O@r_<@~ouCj_VV#UiuV)mCy+~ac@q2;SwVC`ARPO{l)PJ|6QdzDq=Ed3d+>xh( zqHSzUt{rRCc$;$3F3;FzL!s$xUqy*8N3Vw6x8261x!k;YSCn`gYwz*I_IF(TM=}gf zY?Ujp-^@L^eg$h?LG>+*iIYwoF!*@qFxR%s8JaAIZ}=)qS(lwssQ9F@d{Jv|BGcOI zS~;#_EH@NOn^?NH6h8DkDY^W_{YQ6SJiafruXp-K?zQXN9l28TQlLQ&WPu_J6hj~y z7)Qot1|WG5&lnz+;K*~WC~*%i0Y;pHjSWZu2=o&TfE)#VXGaA?1^whiAPJ04pahTv zBB(h=aB~#&Ln;eW74!oV(^C~x6%4`V1|$}x=9K`o!cvKEYD#9JQ+|a)G*HMu!PL+Q zjAOy7o%8cbfXa&%KnV?!s@#k6OAC-31+fDx3Ug;L(B`7Vyy60oUCEU&VNZpUqS90t z&l#x7CABOwIW@@L2_y*di+)gQaeir0a%!;xC`5vgdrx~3-BB4=RcDap*wa{)z-hLM4hfq}V^rJ0URipnVK4#Dx@jE#LUfr5ds1Vd2lgP z6QKXl#7xXF)fpH96Ck=eQ((xUiCGv~Vu)Fq03!vauB0e2GbgnOT=)cMR;2=+42p`N o{QMFHkjEkE#WOE0UjY>I;HWGvNh~S>`_R%7Scs{*`nz!f09k*DVgLXD diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconSettings.imageset/IconSettings.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconSettings.imageset/IconSettings.pdf index 2b2478320483d08c3a2fda56e38250b624f5403b..df159bb49d7726ab847373ffb5990aec8c13e3ad 100644 GIT binary patch delta 743 zcmey#b)TCB^e493I+;6U;#e` zLon@@SyWu2U@}o%wca?`;6c~3uBS{4Y!-~MT&Z~}#U(|liMb$Feo__}NHd5AnrCPR z6SRa1+Sn-Qhg24%D(GX{YG{OHtqGE~M#fN1PDyIfL?h|?iH_MV1_G_`|8?!0qQ!c4 zMe1(XZyWSx~{6Cfqed_dnz1CPK^CZrx<^_v?MhL zhB|($ljPjw_U(xh!-=@k=MR{R1TGwU-jij_D`#-)?X$jPv+{yc6vKj|nP)r-)>5#m zw%_$!XLe@((~4`SmewEp=D8^**|RFQt@#T}pJH>UkiIAZ%gMdYL0sv@1ZL&p7sC=gO(g(#?~1 zt@**aXKu#YtT5RZ(b`%4zZonGALv>dN4oNL&3@h~JvF|``d?H1;j5SGPwQWfPk;P; zf8Vi1@w+7Rm@p#&9G1{nFfuoWMT4O+Mm$6WrKW*{7aI4bmPmoEpzoHGSd!|Jnw+1K z3KkE}tV*p{FoZ-;xI(m{f{}tzELbWiKfgr5&=4Mpo_T5c3Wf$?zMUNxG?i4$Nlr-k z@qd12oWNyl(s_N?R#svUcCkw#< delta 688 zcmZXS%}N6?5XV6;CEmRWb6IQ=yUDh@?MmsPtt~~wvh^k&;x=tp%4SKj(t6p4@JW0C z!8h?ayqTn*(y%PdZzePIpUKbJFDf~sUjLd@{m;_(N#Owo{_=`iEpQ)K83T7jXAB%5 z2*4PEr3#3SiY&NilZ1Bo5}q`|;ML9dY+YTRDjh7LzKb3Ro@dZ%8NW%lu3+gJ72u9F z7BrQaDHX4_p+it2XIrbQqk8NuiCHx07}#!`zZ)^ha}hHMHCt3;mQIWy)AF$}X>Z*n z6R8Dg?FB@;?sFp)?K1=i8AgIn@|X!I$21WfNR|xySLPBa`om^pjd5Ur6>;Fg)IxcCRi_|JJujtr#S~KsO#k&vced|{|_|^$Z^6j)U!5cpO6sT*fS+) uvS7l#sd4hobkBBIoGZ{_o0l|9ryR_TBR%tTBo!6PY9z!Dk>lJx^wA%0AgS8` diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconSpinner.imageset/IconSpinner.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconSpinner.imageset/IconSpinner.pdf index 1856123f209ce381590173ab2ac3a62cd6b2e42d..a8231ffcc3be2e247ba6b9543fedd4bf68031f47 100644 GIT binary patch delta 743 zcmZ3;)yB=C8sOrlYp7?wkt2g?@^5A#ZU3aK;F4S}1qB6tpVYkck_-h41p|d3uz;U} zA((c{EGjNhFqx>XT5lX|@Sy8i*Hb12HVejBuGGAg;*z4 R<7KPig~q!~m5%`-HE z30gu0ZEO_uLn;eW74$J}H8eu9)&$8~BV#BhrzEv#qLEa6=){ZJ%?1L;?*G+(m+B*# z;kUa~_ELM4Ml#QlZ@F(2j~wPY>>+gg*ZucG*~dLN?)1O^R6AXvgM-zkON0H1uwa*| zTPo+xmqtP)KZS6OInd^BF^{q+}Ad+->W-^`a~&MImgHxs<`t=OGR? zWqDilBgHb(pH#e?TJO8{<)oB-$0n5QmHT!{@W{>+tr5?qk0fnA#IE?RQoZ|lyw;B7 z^!st%&US75Vh60Z?!EI;-cP&2wso(HoJ_awn!H!d7cX<9zT}QS&)2B;;>48`2m9`M z#)|#nI_I+`M0EAHFL$-G_^kOYH$LZ$w3T!f>ze(1qV&|+&C&mw>JNw3UplFOH#Yt8 z^Zk9t7RB#U%wxig1#nwsWU0*d44L|?-m9zT=rd`# jH&IXTX=e`5%6Qe`s4A zVAd>4dU|@#J@@qM@*B0G?mc%|+z`2LASrq6nPleZjz4(3C+N2m&x0!Eyye zvtkQ8vQ7BrWIkxKS8 zbq-3DtlmvDYsx;dgvGP50Y@YAcVZ@aDH0~3Z$&lC@|7`UT5cMX_SQqTlG=dwUO^0X z_oXq4P8EW)3JbwkWx@n>5}FAPq(_1thR#N*Lf$%#MN>hGO_sa=1^E#RT%D8F{|EYRlZc7mxa@_)1{UH5k6;p>cuLT0%Y=Pv%j}cso&y^q h=L(GV!xNk5YYt}kv6+liR4D6_-~mDH_TAGx`UUB0uOa{d diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconSuccess.imageset/IconSuccess.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconSuccess.imageset/IconSuccess.pdf index f74a4f6de718f69097bf4849f423909fbc3109f7..faaea423b2e311387ca31cf3880043b3862d7c3d 100644 GIT binary patch delta 743 zcmcb>v5k{MHNeG9*HF)VBgYKJ$=b|9+Wtvd!6ms|3JMDPKB;->B^e493I+;6U;#e` zLon@@SyWu2U@}o%wca?`;6c~3uBS{4Y!-~MT&Z~}#U(|liMb$Feo__}NHd5AnrCPR z6SRa1+Sn-Qhg24%D(GX{YG{OHtqGE~M#fN1PDyIfL?fyC(1{nbn+*hx-T$loF4ade z!*6$~?4|Z7jbxr9-*Vq59y!c)*hA>}ulw(XvX6Uk-06S+sdlqS{mibB{Xb18$P&qEw) z%JR18M~Y>nKdE>(wcdB@%SkEwj!h`pEBEb^;E|mtS|grIA4%GLh+XkrrF!@AcD z>G$Kjo$cEA#SU0+-FxSyyq|W3ZR=hYIhk(VHF>X^FJ9(IeaRhvp082w#fd8?4))#i zj1~LCb3xZ3TU|oWzn;m(=9^ zlvJ>IaAs9~s)8XTe!>-^4Hb+OjAFr3LHYS53WkR8SoF+G%U3Wo0Q2qaxS+|TVoq{G z!jJ#+I~ylAGP)QT8o3!57#MwE=5$k^AYt9HmBGV6vyr2?!^%zI;F%M9*foyT%xq+2 ixT?Y00QIIkQfezMNh~U%bNwLaeVKbh#+eT@`DUlh= z$3#+SxyVJReDyZU3aK;F4S}1qB6tpVYkck_-h41p|d3uz;U} zA((c{EGjNhFqx>XT5lX|@Sy8i*Hb12HVejBuGGAg;*z4 R<7KPig~q!~m5%`-HE z30gu0ZEO_uLn;eW74$J}H8eu9)&$8~BV#BhrzEv#qLEa6=tRft76XCS_y4+fPSIjL zyCQYB>$eShI!i^4yj}Z6;L!uNu8Ats-tB+ya$Q$d(Lg@`(>)aqB`3yyrBjSQU0RZw z14A9Z)k$)0a{Kl~iQzGKE7MFJNNJ@3gf=9M$J_4Zlcu~~URDT-l1(abX*1#2nT zRom}+t}{C`|7pdwQ}s)aee>KDlk8cQ+t&PrrBJU((d?(##v9XqC>*hUpYrvGH*5JD znOoACnnySerhRamJFWWj;Y`-`dw8mSZ7Rg9HS%9AHoeRdD%usG&u1Kav2*3rX6fe1 zyVm?*-7`01ZC04P7K;R}s?Q%j`KR?v6LNi0cqNlngA zNd=1sXI9mxDi}iICtM-gP{BySC>AUgl%HRsU}y-BMbEsnd<8=TFyGFO3z|$S<|HR1 z{P;h=vvG1GqlfzbzMPB*y;64o74S(WZFaH_d=Sh)!tJagg>n@IA!(+3zB iP8?)qg?dvSDYX@sBo>ua6al?pX=KW!s_N?R#svT_j|kNO delta 687 zcmZXS%}&BV5XVEjVDBDH=0b!R+ucG7H4O(q2r))dycuJbvXmOqZFaW?ywrE_=3DrN z?k=CkP19t4GdnZ?nf*Nfu2yT4!SDv#-cNN_DP2IoW^Yy)g7dV=DL507P^ba60JOnr zCIPFc$bbhrPf3rh;aMRBZcBeJ#?@z;RKWsThv*S!MGj%8{W{&Yf}vYff-_Z^lT75g zRJ_@TjzRK*?ybIx8qoJNrqQUUVYjRQPDBMOcuWN}O;LquHrIl5%m0N5bL&2x3nf5l zFCjW~UudD$F@t}SVZzzGh$)A9Oj6DO+t{}e)R$7`;?{8tO*qMyX)J8UlSac->T^nD z%CZ5G6zVQ=5o)^u#?7FGuMxUBL#XlfTDdIyM3NOt%q6`6v77m^oi(Qm3qi+1hXcOn zH9c6so_XyK7%yZ8*7TK5eNLDgsO6oqQXAO&3l(zx{|Wt;nzcSB3EN)#$RsD}igNR& qrs)S&J)2!ICP9VmUeY*QFwirO)XXm|Aqkg8V+RFRt#yA8ACZaC8m)fH=l6j7N%YCDG4py5k4fZF(f?cL= zshl@o8VRIqeRIN**AB8jQCQG;o-;JOI6v77fpRWYv0@z zm~$od;Q9w|J-kCVX8GTb3M{_CdZLp#|HRCTT15eOZ2Ir4_#<=Yqxc``b@h|t>Sn&P zlD7X}Y9jWRY4?ge7R+b>hbA;0jLc175n*VI5fc$XscGN#I@~3?UH|t`KdgV5DFa3ziDX&o5ChG=xW^XI@&qf}sJJZ)e8^O(zv| zk`oes{GZ?1IJuG0#lX<$l#zjf(FbNux3UQm)*Vy1l;$;e*b9{QC^XHSxRX=km<>=J j!|k7}kDxI^g#!rK>zmc>g8j6~DA*ISpil#B0q6~< zsRXQ4p#cvxPRK1=!;3-)u&=+FIqI`iDzSi8sUC5bXVC5HbDeHm!O$%#z@941Nh&g3 zD%$L&V~{+j`(0nD2J}6NXgIpnu-DUnJEVf;JfZ?zQ&eG^#@di>`P7&&QTItKlmX?v zfUxX7*G8>F2LDfn31@L0Q4aNpB%A?mV!w%?zLYW(H@0o)f|G2SM54*~qR}vw`kWG( zuyjBqg}Q?rgaW67vD<0kD}*l35NdorS1yV^kz~adCP?3a*voVIt&3LS+1L4_RuG@70R{aj0f~ZV5@aG#=ZiWz}kTk3;Jhi7Tj< diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconUnobscure.imageset/IconUnobscure.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/IconUnobscure.imageset/IconUnobscure.pdf index ec2d68e3a2cfef7bc5b2ceb833e325d18e539e08..591e0ab5e1a646992f8675d961f5a6557f5b6185 100644 GIT binary patch delta 757 zcmZqSe8tJ38sOrlYp7?wk>d=bt+|4MLVi*fmyL~ren@3Os)D|MQdV$DE|-FWg1%2` zUV2G}f(1}b5Lm!Z!4OQlWfm2eD40xCSFJY=Hh9qWtm`Qg1Dge7ELUn?N^wb1YGN*k z1u__<8AJokGcyA8ACZaC8$<9n2J-oz?x}DnIWhJtonrjy(vs91 z80z?~PLgwz+qWl53@74BpFdzO61Z^ac~6!xubjcHx6k^H&B_Z(Q49-;W}fjVSWCgK z+J4t_o!OcBPb;pSs$Y8So9CvOWY4PHw&pJ^g?dGbW*}JJi>7>?StFgY1N+(XR@x}!&B{RQz2%pk^gG3>1B>k(XIe}KI7Pnohzp{OE*v6 zwdM!wp1B!ov%+LwL~CdD|7Nf(e4uM-9O=r}HT!v|^wjt!>wiu4hp*OOI<0>>KK=3Y z{e8z4#qW~LW5SFDa9~2C!N}Yg77vET7!eT>l$r((Uufi;S|EkCg1%c$Vo9n?YI1%` zDp))?v#LH-!4MKZ;R?})3PuV>v0$m7{QMFHLqm8hdgi6&D;OGp`F3_(&}33ECpjVE z$N%}AjguQ0T?`D3z8M%87=2*oJZ3aO!n&iB)x)5*u_LF$%1z+lnG=5)HI6ZbFfsi2 h!TJ{FOL;_MD=tYaDyb*}`oPe@*pN$A)z#mP3jiXF4;}yj delta 687 zcmZXS&rSj{5XM8iz}`KY%!LRsvhDtZHG~5ogcu`PycrL5DX@lYH*ME|m%aD~K8cTE z;*isc}xYkrl`U+oohq7xxSU2>AGl327DUpAYj z(tuMU6P6B%q|k7XgHYQEF?K@_Um$dTf>87QvvO7ph$L&aG>7yJh~JqX+Szcrun=@C zR4xd7-}PYudlq!m?Dc}QWM0rYVs5C+{nZsZ!2W-zklQ{^*!IFBT_;fU*Gm_coFr?? r&6}DfuT=GH?us!9?ec9*(gg!O<4Dc?)Dn_#X*`D5x9aulyOH$+)=;Tu diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/LocationMarkerSecure.imageset/LocationMarkerSecure.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/LocationMarkerSecure.imageset/LocationMarkerSecure.pdf index ccfb321cace67c7c86ae37fb1f2b69a293f3a3da..01f181c5b97bd4c90930fa8d4545cc302a835d59 100644 GIT binary patch delta 896 zcmaDZ)FsTJ8sOrlYp7>Fk>ePn2@E7-47vGrgNqsyW3d^M z#U@|}KrA*ghH`RBCK|}phfWO4ZZ;5Uz5iGAd8&_OM$+3|d@r4Krx>y&eK-3ikhI~D z!K5P_|NXvu8AJxXO>J`rF7M?_Ir@& z@xAR+ZhUr&w3S?`6J~y1*wo9Meft;I|81+|TZH3x?LOyrzFxN1W&d%ua6aigr PWMp8(rK;-c@5TiHKnpg% delta 922 zcmZvbzmL-}6vx#APDe&Sth}Y71<}|^lP0LT;Zp8&96zcY8)7i2n+BcY$Z^n>B|F=x z13P~J3;zQP1OEdH{|0`hX?rb^rO01@-+T7w*`M$JxpUASkB*ijv4?Ylx;qcJXgXa_R{)6Rc3ID?(*l*?q2Ybv?4sDDycCs8cnPj4x<1%I&zpn2;CZTU%ph14x4wR#@BQ3+6fBo@ z5lJlMBe;bOdhuSOV+X^pHj`#H-drAC8PV`#8cNN|HWMRHAs zu@sXk;SxFt%cKC>qkfM-XQot9KDBM5rjix2EGc_JrggV#N=1@0m5F@B6o-yO96|<8 zNU0n4>0?4364KqQxx4F#DRv=JlMI_)F5A3jcq#eRB0wFBtUjoH&vQMP!cwF`U&F)? z@>x-}z960(iY-u6g@o9t`8&r~I u7hIZ$ah`qRNWt7VR|wtC^@}*or@Cv{(inM{ib-X_IV7M~yZ!9-vGo^H@!|mh diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/LocationMarkerUnsecure.imageset/LocationMarkerUnsecure.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/LocationMarkerUnsecure.imageset/LocationMarkerUnsecure.pdf index e407bb352a7e78d905f72f883f26c02295cec45c..e125e79ad9e6d6ba4eee66d8b053cf807d2af9bb 100644 GIT binary patch delta 1128 zcmaDV)G5rN8sOrlYp7>Fk;9{&$&hF7^YA^J6V!cgOFq|X+OVPh?&=B&5m84M+c*FI z`2KpRv$Qhvp7*C$2dk#VIiI(<^TP2>nDXz9?AD$(>v&XEBtP0Lo-OfQe5IK|-ZkOs z>1|TpH`xxdi#IRzow%~uDY0lt(AFy9uQuDCwVkfM>3Q~PW0pm7;T_*N-G#qpH`MRE z99}!QeeQc*7W*SVE>thQ8&`kWBIt;E(%kGBF_SJV5V*L_yQEM;XZm)pRmMl1f+vf* z^~~8i@xSlN_^j2}xlRADPy664$(5RyQe0A$nwSe>ET*_Sa+%Ur=6C?2F>XU7FZPy;OCN)_~dQuESFG87CAHm_mIW#%_DKp1Xj zVLEvNlQff|;p8<;i+Bye<_7yF7H2D1PX5HEA7W^TP;6)dGu;Bo^pMJeR0Vziq^#hQ zT(EYSbw)r%L0}O-1&G0JnMK7V3MP{`FiB2y;HftbHh9qWtm`Qg1Dge7EXZusKrjTE z01X90Gnn0mh)___$7#74lI12~M?fq$GKO+;N+$1Q5&$_PbYft3vw=wK{lB8mQ+*^e zlHTs(d+Dq@#gHxOyV*B^qz#7*CLP)M@Aus!w+w}tzCXS*r&yRz>?2dQ*wY6kpG?D~ z<)*4``hI%FgsY2!&6p7?f8R3s=wp4+ zcPDOczkWzV{xCcD1K+KCZ@iZGI-OzLwpT??rc<|T_OFaDCW~}xAHKe+c!70phm_%s zi`FMkH^o0Vvt-&TrK^s$--Aq#?`@xQj9bF!S@mre5ak+rO~>Z(AMTA{@VK z_c^!o^|HM#`;R;3abbxGP;kQ1qd7c18iEoYNZ7^(78MaescFcmz}yfR+p_h+nN_I@ zh8AF%aD`|?1tSHcSTHXrKfgr55R~2_X~i=yEnmUV1k48}XK0G3n3J54@Zf$QQfHq3(c3RRMhEXK<2!T~+W j`fAL~423})SiDwTl2}wyQ3Q0gk&%HRm#V6(zZ(|--3FkC delta 1131 zcmZXTZAep57{{5Jj?tHtQi$q64L7sxp1a+*|gcX@mP}n zwYlE%)ZoI>__x~imBL@ffw80GD}x0$>Sqp8;o_lF-isY~r%D31UhnT-&YR0G8klHr zelX=iwfEbmdy7#2)6v1t-OJ|}Z(i-1>4-VQskWCt-uz5ezHRJF4vl>@Yrnrbvv%@b z*VGGc-SFwa<3*<4j{!?ql6+8jYut66}gBM zC8c~ls_X%>7(?pJja?P!67NIaY8Mgl2}rOPNkUBYAqiNNqUnR#@LWAuJ&FY7T_Ipk z3cK$;iRaAcl-f6!aT3t$#uu)^u1SMsAb-Z7~ zqzGg$P$|U-(2N#90#eP;BntkR4~f9w<9Ja3&;(f%1q>lsjwYlNX0sv(MJ^iReUeEK zgGQrL$}J*J=7mTFCnI2>DVm~ev;#tm!wQd4R0%~HGizq=+Re#aSO_Y~$Q_V2yPigx zh>IwoqX49J)Yb=BhOsap05&yfw_=Lq0})lDtX9l>hh~A7#2f33*EdT%j diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/LogoIcon.imageset/LogoIcon.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/LogoIcon.imageset/LogoIcon.pdf index 71201faeb991343808162be36bcf53944d7b82fc..d1703ba9e972f44941a49f37f854853c0f0def37 100644 GIT binary patch delta 760 zcmdnSKSzK=HNeG9*HF)VBgbzRTXO{ih5V!}E*l#K{gBFnR0Vziq^#hQTrLF#1%02? zy!4U`1q+~>Ah3X+f+3i8%PcA`Q81aPu3B##Z1AA#S=Uo01~v=ESgzE(l;V=2)Wlp6 z3uG`zGl&M7XJ`f!v?Rh-Ln9<>O^~cLGKO+;N>Ym^8cEfMPCT1+*g&A=eO=eyDYry& zJpDX^efSC$rUV)IwEV?_+M{*xVQd(pZeK- zb`Q*CI_nsl+VZk6;{hC;(1k;jN(WmXlbL>XMqA zpOOj|53bLwN>wm~#89|Gw4s8Lf>A73Dkwj{M8VJy9*>@RY559<24KFO9TzmARLn_E zNci!8erMz4Mn)F{!%c6}5)x7#unXyUJ0!hv5Ysm9$Y2%Rtnf<0k;Pbfw^BgQ-KIuH jhOdRJ0bp+`p`^FslEk8tiXxyF3@uD7xKveL{oS|#+E@)` delta 690 zcmZXSO-sWt7{@OYq!>6Q0X(LtK;8c2&j>+f+ovF};b0*k-dbnv$e4z4SwP^-Fm1 zvk6|z6ZaxppoQm`|MQ-Y-LIYP`fzZ5L|ftK_D`*1KtMh|dz}vWcdvy2f5;~S>OegJ zG?d55?Yoj5gG!Q)MAmR*b)}K*_w7vPSX5tPtKKR?^e@6t$+S#_f=0qKC4shR)FRM$GN#a{zVGNn@!}~vr1}0`+pqfE@Tr_QC)|OaPkmL+Oo_2aAoj@e zKoo{S2xFKt8WP(!9>|xB#db97jIQY_6wq)(NaJ{eg^``iTEHl_?_t4=;@MOv_o{BQ pH(_gbyOPpC+pP)B%X};WXFReq-}AKL%D5hBJ78YDes**2{Q@2Usy6@t diff --git a/ios/MullvadVPN/Supporting Files/Assets.xcassets/LogoText.imageset/LogoText.pdf b/ios/MullvadVPN/Supporting Files/Assets.xcassets/LogoText.imageset/LogoText.pdf index 90f2c0a132782c4866e2c13ad6437a631c4838e0..58ee1f9ad1bd8e6f89984c95ec0b9a7d3b507054 100644 GIT binary patch delta 753 zcmew_^jMffHNeG9*HF)VBgbyG$&nmF+Wtvd!6ms|3JMDPKB;->B^e493I+;6U;#e` zLon@@SyWu2U@}o%wca?`;6c~3uBS{4Y!-~MT&Z~}#U(|liMb$Feo__}NHd5AnrCPR z6SRa1+Sn-Qhg24%D(GX{YG{OHtqGE~M#fN1PDyIfL?fyC(20TB%?2W^_y3AMPxX<^ zSoAik{mVkJQxeRXzs9wSZ{hW>zu^1O(Jvh&P~j{ zyg*|@Ox|9npiZ}&3=W^JT@#b{I9PD#d54#637^f4w{EVfCAJ=^0$vxTc;!Clo)L(f z`}(c-q@FE?i}r83bgcfCNuWt>@|1$L@AwXBwrF}w-d~-rPu=c`e|ToWvsFvN*}vZPDz$oNyX3>X`BSboPdud*ZF62_N`2m|gO^PH zeU0Q&-&azcKUuBt{Uo`+OtPv4p_tJD4pnG;7+Dy>BE--bBSs>EQq#Zz4NV26mPo;_ zpzoHGSd!|Jnw+0fp9+=;&a6sRFoZ-@xI(m{f{}tzELbKeKfgr5&=4M-o_T5c3Wf$? zzMUNxG`&>JNlr-k@qd12(0o-^j@DIa-r#C8j@%OA?DpDvE$!FfukU=2BI4^>^a}0Ey}sr~m)} delta 699 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 Bool { switch itemIdentifier(for: indexPath) { - case .vpnSettings, .problemReport, .faq, .apiAccess: + case .vpnSettings, .problemReport, .faq, .apiAccess, .multihop: true case .version, .daita, .daitaDirectOnly, .none: false @@ -159,17 +162,17 @@ final class SettingsDataSource: UITableViewDiffableDataSource() if interactor.deviceState.isLoggedIn { - snapshot.appendSections([.daita]) - snapshot.appendItems([.daita, .daitaDirectOnly], toSection: .daita) - } - - snapshot.appendSections([.main]) - - if interactor.deviceState.isLoggedIn { - snapshot.appendItems([.vpnSettings], toSection: .main) + snapshot.appendSections([.vpnSettings]) + snapshot.appendItems([ + .daita, + .daitaDirectOnly, + .multihop, + .vpnSettings, + ], toSection: .vpnSettings) } - snapshot.appendItems([.apiAccess], toSection: .main) + snapshot.appendSections([.apiAccess]) + snapshot.appendItems([.apiAccess], toSection: .apiAccess) snapshot.appendSections([.version, .problemReport]) snapshot.appendItems([.version], toSection: .version) diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift b/ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift index 01b4df8ea6cb..bc60b50a4082 100644 --- a/ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift +++ b/ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift @@ -156,6 +156,8 @@ extension SettingsDataSource.Item { return .faq case .apiAccess: return .apiAccess + case .multihop: + return .multihop } } } diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift index 526fb1c16c8c..f28f00e15245 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift @@ -212,6 +212,7 @@ final class VPNSettingsCellFactory: CellFactoryProtocol { ) cell.accessibilityIdentifier = "\(item.accessibilityIdentifier.rawValue)\(portString)" cell.applySubCellStyling() + case .quantumResistanceAutomatic: guard let cell = cell as? SelectableSettingsCell else { return } @@ -235,6 +236,7 @@ final class VPNSettingsCellFactory: CellFactoryProtocol { ) cell.accessibilityIdentifier = item.accessibilityIdentifier cell.applySubCellStyling() + case .quantumResistanceOff: guard let cell = cell as? SelectableSettingsCell else { return } @@ -246,27 +248,6 @@ final class VPNSettingsCellFactory: CellFactoryProtocol { ) cell.accessibilityIdentifier = item.accessibilityIdentifier cell.applySubCellStyling() - - case .multihopSwitch: - guard let cell = cell as? SettingsSwitchCell else { return } - - cell.titleLabel.text = NSLocalizedString( - "MULTIHOP_LABEL", - tableName: "VPNSettings", - value: "Multihop", - comment: "" - ) - cell.accessibilityIdentifier = item.accessibilityIdentifier - cell.setOn(viewModel.multihopState.isEnabled, animated: false) - - cell.infoButtonHandler = { [weak self] in - self?.delegate?.showInfo(for: .multihop) - } - - cell.action = { [weak self] isEnabled in - let state: MultihopState = isEnabled ? .on : .off - self?.delegate?.switchMultihop(state) - } } } } diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift index 4adf52ce2654..6d35059dba2c 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift @@ -24,8 +24,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< case wireGuardObfuscationOption case wireGuardObfuscationPort case quantumResistance - case multihop - case daita + var reusableViewClass: AnyClass { switch self { case .dnsSettings: @@ -44,10 +43,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< return SelectableSettingsCell.self case .quantumResistance: return SelectableSettingsCell.self - case .multihop: - return SettingsSwitchCell.self - case .daita: - return SettingsSwitchCell.self } } } @@ -83,7 +78,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< case quantumResistanceAutomatic case quantumResistanceOn case quantumResistanceOff - case multihopSwitch static var wireGuardPorts: [Item] { let defaultPorts = VPNSettingsViewModel.defaultWireGuardPorts.map { @@ -144,8 +138,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< return .quantumResistanceOn case .quantumResistanceOff: return .quantumResistanceOff - case .multihopSwitch: - return .multihopSwitch } } @@ -167,8 +159,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< return .wireGuardObfuscationPort case .quantumResistanceAutomatic, .quantumResistanceOn, .quantumResistanceOff: return .quantumResistance - case .multihopSwitch: - return .multihop } } } @@ -450,8 +440,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< snapshot.appendItems([.dnsSettings], toSection: .dnsSettings) snapshot.appendItems([.ipOverrides], toSection: .ipOverrides) - snapshot.appendItems([.multihopSwitch], toSection: .privacyAndSecurity) - applySnapshot(snapshot, animated: animated, completion: completion) } @@ -627,7 +615,8 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< /* Since we are dequeuing headers, it's crucial to maintain the state of expansion. - Using screenshots as a single source of truth to capture the state allows us to determine whether headers are expanded or not. + Using screenshots as a single source of truth to capture the state allows us to + determine whether headers are expanded or not. */ private func isExpanded(_ section: Section) -> Bool { let snapshot = snapshot() diff --git a/ios/MullvadVPN/Views/CustomToggleStyle.swift b/ios/MullvadVPN/Views/CustomToggleStyle.swift new file mode 100644 index 000000000000..f5940391605b --- /dev/null +++ b/ios/MullvadVPN/Views/CustomToggleStyle.swift @@ -0,0 +1,66 @@ +// +// CustomToggle.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-11-13. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +/// Custom (default) toggle style used for switches. +struct CustomToggleStyle: ToggleStyle { + private let width: CGFloat = 48 + private let height: CGFloat = 30 + private let circleRadius: CGFloat = 23 + + var infoButtonAction: (() -> Void)? + + func makeBody(configuration: Configuration) -> some View { + HStack { + configuration.label + + if let infoButtonAction { + Button(action: infoButtonAction) { + Image(.iconInfo) + } + .adjustingTapAreaSize() + .tint(.white) + } + + Spacer() + + ZStack(alignment: configuration.isOn ? .trailing : .leading) { + Capsule(style: .circular) + .frame(width: width, height: height) + .foregroundColor(.clear) + .overlay( + RoundedRectangle(cornerRadius: circleRadius) + .stroke( + Color(.white.withAlphaComponent(0.8)), + lineWidth: 2 + ) + ) + + Circle() + .frame(width: circleRadius, height: circleRadius) + .padding(3) + .foregroundColor( + configuration.isOn + ? Color(uiColor: UIColor.Switch.onThumbColor) + : Color(uiColor: UIColor.Switch.offThumbColor) + ) + } + .onTapGesture { + toggle(configuration) + } + .adjustingTapAreaSize() + } + } + + private func toggle(_ configuration: Configuration) { + withAnimation(.easeInOut(duration: 0.25)) { + configuration.$isOn.wrappedValue.toggle() + } + } +} diff --git a/ios/MullvadVPN/Views/IncreasedHitButton.swift b/ios/MullvadVPN/Views/IncreasedHitButton.swift index e3a79838d637..d85cce07b557 100644 --- a/ios/MullvadVPN/Views/IncreasedHitButton.swift +++ b/ios/MullvadVPN/Views/IncreasedHitButton.swift @@ -6,6 +6,7 @@ // Copyright © 2023 Mullvad VPN AB. All rights reserved. // +import SwiftUI import UIKit final class IncreasedHitButton: UIButton { diff --git a/ios/convert-assets.rb b/ios/convert-assets.rb index 06401408d362..5bfdda7ed8e6 100755 --- a/ios/convert-assets.rb +++ b/ios/convert-assets.rb @@ -43,6 +43,7 @@ "icon-copy.svg", "icon-obscure.svg", "icon-unobscure.svg", + "multihop-illustration.svg" ] # graphical assets to resize.