diff options
| author | Bug Magnet <marco.nikic@mullvad.net> | 2024-04-11 13:31:30 +0200 |
|---|---|---|
| committer | Bug Magnet <marco.nikic@mullvad.net> | 2024-04-11 13:31:30 +0200 |
| commit | c2cf8b544753127eeeb16167ef1f19d5daae609a (patch) | |
| tree | 86348d03fdb39d483f5c652b4cc135c1a76af8ec | |
| parent | faae234457d381967911654e5b262734025d3ea8 (diff) | |
| parent | 127b3369d5277883c850a3d5f6d2a619b9e3de5f (diff) | |
| download | mullvadvpn-c2cf8b544753127eeeb16167ef1f19d5daae609a.tar.xz mullvadvpn-c2cf8b544753127eeeb16167ef1f19d5daae609a.zip | |
Merge branch 'move-up-the-title-of-server-ip-override-ios-558'
11 files changed, 199 insertions, 80 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index fcd39ee55e..7879654647 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -482,6 +482,7 @@ 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 */; }; @@ -1750,6 +1751,7 @@ 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>"; }; @@ -3502,6 +3504,7 @@ isa = PBXGroup; children = ( 7A5869AA2B55527C00640D27 /* IPOverrideCoordinator.swift */, + 7A28826C2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift */, 7AB4CCBA2B691BBB006037F5 /* IPOverrideInteractor.swift */, 7A5869BE2B57D0A100640D27 /* IPOverrideStatus.swift */, 7A5869C02B57D21A00640D27 /* IPOverrideStatusView.swift */, @@ -5333,6 +5336,7 @@ 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 */, 583FE01029C0F532006E85F9 /* CustomSplitViewController.swift in Sources */, 7A6F2FA92AFD0842006D0856 /* CustomDNSDataSource.swift in Sources */, diff --git a/ios/MullvadVPN/Containers/Navigation/UINavigationBar+Appearance.swift b/ios/MullvadVPN/Containers/Navigation/UINavigationBar+Appearance.swift index b701159947..68d503a076 100644 --- a/ios/MullvadVPN/Containers/Navigation/UINavigationBar+Appearance.swift +++ b/ios/MullvadVPN/Containers/Navigation/UINavigationBar+Appearance.swift @@ -32,7 +32,7 @@ extension UINavigationBar { private func makeNavigationBarAppearance(isTransparent: Bool) -> UINavigationBarAppearance { let backIndicatorImage = UIImage(named: "IconBack")?.withTintColor( - UIColor.NavigationBar.backButtonIndicatorColor, + UIColor.NavigationBar.buttonColor, renderingMode: .alwaysOriginal ) let backIndicatorTransitionMask = UIImage(named: "IconBackTransitionMask") diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentConfiguration.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentConfiguration.swift index 891927c510..7055407625 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentConfiguration.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentConfiguration.swift @@ -38,7 +38,12 @@ struct ListCellContentConfiguration: UIContentConfiguration, Equatable { let tertiaryTextProperties = TertiaryTextProperties() /// Content view layout margins. - var directionalLayoutMargins: NSDirectionalEdgeInsets = UIMetrics.SettingsCell.apiAccessInsetLayoutMargins + var directionalLayoutMargins = NSDirectionalEdgeInsets( + top: 8, + leading: 24, + bottom: 8, + trailing: 24 + ) func makeContentView() -> UIView & UIContentView { return ListCellContentView(configuration: self) diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentConfiguration.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentConfiguration.swift index 2eddbf0410..0f652febe7 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentConfiguration.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentConfiguration.swift @@ -28,7 +28,7 @@ struct TextCellContentConfiguration: UIContentConfiguration, Equatable { /// The editing events configuration. var editingEvents = EditingEvents() - /// The text properties confgiuration. + /// The text properties configuration. var textProperties = TextProperties() /// The text field properties configuration. diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift index 748b3286aa..f5383c6d19 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift @@ -76,17 +76,20 @@ class ListAccessMethodCoordinator: Coordinator, Presenting, SettingsChildCoordin private func about() { let header = NSLocalizedString( "ABOUT_API_ACCESS_HEADER", + tableName: "APIAccess", value: "API access", comment: "" ) let preamble = NSLocalizedString( "ABOUT_API_ACCESS_PREAMBLE", + tableName: "APIAccess", value: "Manage default and setup custom methods to access the Mullvad API.", comment: "" ) let body = [ NSLocalizedString( "ABOUT_API_ACCESS_BODY_1", + tableName: "APIAccess", value: """ The app needs to communicate with a Mullvad API server to log you in, fetch server lists, \ and other critical operations. @@ -95,6 +98,7 @@ class ListAccessMethodCoordinator: Coordinator, Presenting, SettingsChildCoordin ), NSLocalizedString( "ABOUT_API_ACCESS_BODY_2", + tableName: "APIAccess", value: """ On some networks, where various types of censorship are being used, the API servers might \ not be directly reachable. @@ -103,6 +107,7 @@ class ListAccessMethodCoordinator: Coordinator, Presenting, SettingsChildCoordin ), NSLocalizedString( "ABOUT_API_ACCESS_BODY_3", + tableName: "APIAccess", value: """ This feature allows you to circumvent that censorship by adding custom ways to access the \ API via proxies and similar methods. diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift index e4d6f504d2..b2f9fe5164 100644 --- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift @@ -47,4 +47,53 @@ extension IPOverrideCoordinator: IPOverrideViewControllerDelegate { presentationContext.present(customNavigationController, animated: true) } + + func presentAbout() { + let header = NSLocalizedString( + "IP_OVERRIDE_HEADER", + tableName: "IPOverride", + value: "IP Override", + comment: "" + ) + let body = [ + NSLocalizedString( + "IP_OVERRIDE_BODY_1", + tableName: "IPOverride", + value: """ + On some networks, where various types of censorship are being used, our server IP addresses are \ + sometimes blocked. + """, + comment: "" + ), + NSLocalizedString( + "IP_OVERRIDE_BODY_2", + tableName: "IPOverride", + value: """ + To circumvent this you can import a file or a text, provided by our support team, \ + with new IP addresses that override the default addresses of the servers in the Select location view. + """, + comment: "" + ), + NSLocalizedString( + "IP_OVERRIDE_BODY_3", + tableName: "IPOverride", + value: """ + If you are having issues connecting to VPN servers, please contact support. + """, + comment: "" + ), + ] + + let aboutController = AboutViewController(header: header, preamble: nil, body: 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) + } } diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideHeaderView.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideHeaderView.swift new file mode 100644 index 0000000000..16c751f121 --- /dev/null +++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideHeaderView.swift @@ -0,0 +1,115 @@ +// +// IPOverrideHeaderView.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-03-20. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +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. + 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 = .zero + + 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( + "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] = "#" + + 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/IPOverride/IPOverrideViewController.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift index 1088a86a2d..be9116823d 100644 --- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift +++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift @@ -13,6 +13,7 @@ class IPOverrideViewController: UIViewController { private let interactor: IPOverrideInteractor private var cancellables = Set<AnyCancellable>() private let alertPresenter: AlertPresenter + private let headerView = IPOverrideHeaderView() weak var delegate: IPOverrideViewControllerDelegate? @@ -51,11 +52,11 @@ class IPOverrideViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - navigationController?.navigationItem.largeTitleDisplayMode = .never view.backgroundColor = .secondaryColor + view.directionalLayoutMargins = UIMetrics.contentHeadingLayoutMargins - addHeader() - addPreamble() + configureNavigation() + addHeaderView() addImportButtons() addStatusLabel() @@ -70,44 +71,21 @@ class IPOverrideViewController: UIViewController { }.store(in: &cancellables) } - private func addHeader() { - let label = UILabel() - label.font = .systemFont(ofSize: 32, weight: .bold) - label.textColor = .white - label.text = NSLocalizedString( + private func configureNavigation() { + title = NSLocalizedString( "IP_OVERRIDE_HEADER", tableName: "IPOverride", value: "Server IP override", comment: "" ) - - let infoButton = UIButton(type: .custom) - infoButton.tintColor = .white - infoButton.setImage(UIImage(resource: .iconInfo), for: .normal) - infoButton.addTarget(self, action: #selector(didTapInfoButton), for: .touchUpInside) - infoButton.heightAnchor.constraint(equalToConstant: 24).isActive = true - infoButton.widthAnchor.constraint(equalTo: infoButton.heightAnchor, multiplier: 1).isActive = true - - let headerView = UIStackView(arrangedSubviews: [label, infoButton, UIView()]) - headerView.spacing = 8 - - containerView.addArrangedSubview(headerView) - containerView.setCustomSpacing(14, after: headerView) } - private func addPreamble() { - let label = UILabel() - label.font = .systemFont(ofSize: 12, weight: .semibold) - label.textColor = .white.withAlphaComponent(0.6) - label.numberOfLines = 0 - label.text = NSLocalizedString( - "IP_OVERRIDE_PREAMBLE", - tableName: "IPOverride", - value: "Import files or text with new IP addresses for the servers in the Select location view.", - comment: "" - ) + private func addHeaderView() { + headerView.onAbout = { [weak self] in + self?.delegate?.presentAbout() + } - containerView.addArrangedSubview(label) + containerView.addArrangedSubview(headerView) } private func addImportButtons() { @@ -140,44 +118,6 @@ class IPOverrideViewController: UIViewController { containerView.addArrangedSubview(statusView) } - @objc private func didTapInfoButton() { - let message = NSLocalizedString( - "IP_OVERRIDE_DIALOG_MESSAGE", - tableName: "IPOverride", - value: """ - On some networks, where various types of censorship are being used, our server IP addresses are \ - sometimes blocked. - To circumvent this you can import a file or a text, provided by our support team, \ - with new IP addresses that override the default addresses of the servers in the Select location view. - If you are having issues connecting to VPN servers, please contact support. - """, - comment: "" - ) - - let presentation = AlertPresentation( - id: "ip-override-info-alert", - icon: .info, - title: NSLocalizedString( - "IP_OVERRIDE_INFO_DIALOG_TITLE", - tableName: "IPOverride", - value: "Server IP override", - comment: "" - ), - message: message, - buttons: [AlertAction( - title: NSLocalizedString( - "IP_OVERRIDE_INFO_DIALOG_OK_BUTTON", - tableName: "IPOverride", - value: "Got it!", - comment: "" - ), - style: .default - )] - ) - - alertPresenter.showAlert(presentation: presentation, animated: true) - } - @objc private func didTapClearButton() { let presentation = AlertPresentation( id: "ip-override-clear-alert", diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewControllerDelegate.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewControllerDelegate.swift index d71de543c4..5f24c056c4 100644 --- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewControllerDelegate.swift +++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewControllerDelegate.swift @@ -10,4 +10,5 @@ import Foundation protocol IPOverrideViewControllerDelegate: AnyObject { func presentImportTextController() + func presentAbout() } diff --git a/ios/MullvadVPN/UI appearance/UIColor+Palette.swift b/ios/MullvadVPN/UI appearance/UIColor+Palette.swift index 63495b44a8..5cc9a562a1 100644 --- a/ios/MullvadVPN/UI appearance/UIColor+Palette.swift +++ b/ios/MullvadVPN/UI appearance/UIColor+Palette.swift @@ -72,7 +72,7 @@ extension UIColor { // Navigation bars enum NavigationBar { - static let backButtonIndicatorColor = UIColor(white: 1.0, alpha: 0.8) + static let buttonColor = UIColor(white: 1.0, alpha: 0.8) static let backButtonTitleColor = UIColor.white static let titleColor = UIColor.white static let promptColor = UIColor.white diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift index a3f5b16747..ed4f2727b3 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift @@ -377,11 +377,11 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { let sectionIdentifier = snapshot().sectionIdentifiers[section] - switch sectionIdentifier { - case .ipOverrides: - return 10 - default: - return 0.5 + return switch sectionIdentifier { + // 0 due to there already being a separator between .dnsSettings and .ipOverrides. + case .dnsSettings: 0 + case .ipOverrides: 10 + default: 0.5 } } |
