Skip to content

Commit 7c1f112

Browse files
Jon Peterssonbuggmagnet
Jon Petersson
authored andcommitted
Move the title 'Server IP Override' up
1 parent 848a1a2 commit 7c1f112

11 files changed

+199
-80
lines changed

Diff for: ios/MullvadVPN.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@
482482
7A1A26492A29D48A00B978AA /* RelayFilterCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A26482A29D48A00B978AA /* RelayFilterCellFactory.swift */; };
483483
7A21DACF2A30AA3700A787A9 /* UITextField+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */; };
484484
7A28826A2BA8336600FD9F20 /* VPNSettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2882692BA8336600FD9F20 /* VPNSettingsCoordinator.swift */; };
485+
7A28826D2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28826C2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift */; };
485486
7A2960F62A963F7500389B82 /* AlertCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2960F52A963F7500389B82 /* AlertCoordinator.swift */; };
486487
7A2960FD2A964BB700389B82 /* AlertPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2960FC2A964BB700389B82 /* AlertPresentation.swift */; };
487488
7A307AD92A8CD8DA0017618B /* Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A307AD82A8CD8DA0017618B /* Duration.swift */; };
@@ -1751,6 +1752,7 @@
17511752
7A1A264A2A29D65E00B978AA /* SelectableSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableSettingsCell.swift; sourceTree = "<group>"; };
17521753
7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Appearance.swift"; sourceTree = "<group>"; };
17531754
7A2882692BA8336600FD9F20 /* VPNSettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNSettingsCoordinator.swift; sourceTree = "<group>"; };
1755+
7A28826C2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideHeaderView.swift; sourceTree = "<group>"; };
17541756
7A2960F52A963F7500389B82 /* AlertCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertCoordinator.swift; sourceTree = "<group>"; };
17551757
7A2960FC2A964BB700389B82 /* AlertPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertPresentation.swift; sourceTree = "<group>"; };
17561758
7A307AD82A8CD8DA0017618B /* Duration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Duration.swift; sourceTree = "<group>"; };
@@ -3505,6 +3507,7 @@
35053507
isa = PBXGroup;
35063508
children = (
35073509
7A5869AA2B55527C00640D27 /* IPOverrideCoordinator.swift */,
3510+
7A28826C2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift */,
35083511
7AB4CCBA2B691BBB006037F5 /* IPOverrideInteractor.swift */,
35093512
7A5869BE2B57D0A100640D27 /* IPOverrideStatus.swift */,
35103513
7A5869C02B57D21A00640D27 /* IPOverrideStatusView.swift */,
@@ -5337,6 +5340,7 @@
53375340
F0C6FA852A6A733700F521F0 /* InAppPurchaseInteractor.swift in Sources */,
53385341
58CEB2F92AFD136E00E6E088 /* UIBackgroundConfiguration+Extensions.swift in Sources */,
53395342
5878F50029CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift in Sources */,
5343+
7A28826D2BAAC9DE00FD9F20 /* IPOverrideHeaderView.swift in Sources */,
53405344
A98502032B627B120061901E /* LocalNetworkProbe.swift in Sources */,
53415345
583FE01029C0F532006E85F9 /* CustomSplitViewController.swift in Sources */,
53425346
7A6F2FA92AFD0842006D0856 /* CustomDNSDataSource.swift in Sources */,

Diff for: ios/MullvadVPN/Containers/Navigation/UINavigationBar+Appearance.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extension UINavigationBar {
3232

3333
private func makeNavigationBarAppearance(isTransparent: Bool) -> UINavigationBarAppearance {
3434
let backIndicatorImage = UIImage(named: "IconBack")?.withTintColor(
35-
UIColor.NavigationBar.backButtonIndicatorColor,
35+
UIColor.NavigationBar.buttonColor,
3636
renderingMode: .alwaysOriginal
3737
)
3838
let backIndicatorTransitionMask = UIImage(named: "IconBackTransitionMask")

Diff for: ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentConfiguration.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ struct ListCellContentConfiguration: UIContentConfiguration, Equatable {
3838
let tertiaryTextProperties = TertiaryTextProperties()
3939

4040
/// Content view layout margins.
41-
var directionalLayoutMargins: NSDirectionalEdgeInsets = UIMetrics.SettingsCell.apiAccessInsetLayoutMargins
41+
var directionalLayoutMargins = NSDirectionalEdgeInsets(
42+
top: 8,
43+
leading: 24,
44+
bottom: 8,
45+
trailing: 24
46+
)
4247

4348
func makeContentView() -> UIView & UIContentView {
4449
return ListCellContentView(configuration: self)

Diff for: ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentConfiguration.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ struct TextCellContentConfiguration: UIContentConfiguration, Equatable {
2828
/// The editing events configuration.
2929
var editingEvents = EditingEvents()
3030

31-
/// The text properties confgiuration.
31+
/// The text properties configuration.
3232
var textProperties = TextProperties()
3333

3434
/// The text field properties configuration.

Diff for: ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift

+5
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,20 @@ class ListAccessMethodCoordinator: Coordinator, Presenting, SettingsChildCoordin
7676
private func about() {
7777
let header = NSLocalizedString(
7878
"ABOUT_API_ACCESS_HEADER",
79+
tableName: "APIAccess",
7980
value: "API access",
8081
comment: ""
8182
)
8283
let preamble = NSLocalizedString(
8384
"ABOUT_API_ACCESS_PREAMBLE",
85+
tableName: "APIAccess",
8486
value: "Manage default and setup custom methods to access the Mullvad API.",
8587
comment: ""
8688
)
8789
let body = [
8890
NSLocalizedString(
8991
"ABOUT_API_ACCESS_BODY_1",
92+
tableName: "APIAccess",
9093
value: """
9194
The app needs to communicate with a Mullvad API server to log you in, fetch server lists, \
9295
and other critical operations.
@@ -95,6 +98,7 @@ class ListAccessMethodCoordinator: Coordinator, Presenting, SettingsChildCoordin
9598
),
9699
NSLocalizedString(
97100
"ABOUT_API_ACCESS_BODY_2",
101+
tableName: "APIAccess",
98102
value: """
99103
On some networks, where various types of censorship are being used, the API servers might \
100104
not be directly reachable.
@@ -103,6 +107,7 @@ class ListAccessMethodCoordinator: Coordinator, Presenting, SettingsChildCoordin
103107
),
104108
NSLocalizedString(
105109
"ABOUT_API_ACCESS_BODY_3",
110+
tableName: "APIAccess",
106111
value: """
107112
This feature allows you to circumvent that censorship by adding custom ways to access the \
108113
API via proxies and similar methods.

Diff for: ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideCoordinator.swift

+49
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,53 @@ extension IPOverrideCoordinator: IPOverrideViewControllerDelegate {
4747

4848
presentationContext.present(customNavigationController, animated: true)
4949
}
50+
51+
func presentAbout() {
52+
let header = NSLocalizedString(
53+
"IP_OVERRIDE_HEADER",
54+
tableName: "IPOverride",
55+
value: "IP Override",
56+
comment: ""
57+
)
58+
let body = [
59+
NSLocalizedString(
60+
"IP_OVERRIDE_BODY_1",
61+
tableName: "IPOverride",
62+
value: """
63+
On some networks, where various types of censorship are being used, our server IP addresses are \
64+
sometimes blocked.
65+
""",
66+
comment: ""
67+
),
68+
NSLocalizedString(
69+
"IP_OVERRIDE_BODY_2",
70+
tableName: "IPOverride",
71+
value: """
72+
To circumvent this you can import a file or a text, provided by our support team, \
73+
with new IP addresses that override the default addresses of the servers in the Select location view.
74+
""",
75+
comment: ""
76+
),
77+
NSLocalizedString(
78+
"IP_OVERRIDE_BODY_3",
79+
tableName: "IPOverride",
80+
value: """
81+
If you are having issues connecting to VPN servers, please contact support.
82+
""",
83+
comment: ""
84+
),
85+
]
86+
87+
let aboutController = AboutViewController(header: header, preamble: nil, body: body)
88+
let aboutNavController = UINavigationController(rootViewController: aboutController)
89+
90+
aboutController.navigationItem.rightBarButtonItem = UIBarButtonItem(
91+
systemItem: .done,
92+
primaryAction: UIAction { [weak aboutNavController] _ in
93+
aboutNavController?.dismiss(animated: true)
94+
}
95+
)
96+
97+
navigationController.present(aboutNavController, animated: true)
98+
}
5099
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
//
2+
// IPOverrideHeaderView.swift
3+
// MullvadVPN
4+
//
5+
// Created by Jon Petersson on 2024-03-20.
6+
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
/// Header view pinned at the top of ``IPOverrideViewController``.
12+
class IPOverrideHeaderView: UIView, UITextViewDelegate {
13+
/// Event handler invoked when user taps on the link to learn more about API access.
14+
var onAbout: (() -> Void)?
15+
16+
private let textView = UITextView()
17+
18+
override init(frame: CGRect) {
19+
super.init(frame: frame)
20+
21+
textView.backgroundColor = .clear
22+
textView.dataDetectorTypes = .link
23+
textView.isSelectable = true
24+
textView.isEditable = false
25+
textView.isScrollEnabled = false
26+
textView.contentInset = .zero
27+
textView.textContainerInset = .zero
28+
textView.attributedText = makeAttributedString()
29+
textView.linkTextAttributes = defaultLinkAttributes
30+
textView.textContainer.lineFragmentPadding = 0
31+
textView.delegate = self
32+
33+
directionalLayoutMargins = .zero
34+
35+
addSubviews()
36+
}
37+
38+
required init?(coder: NSCoder) {
39+
fatalError("init(coder:) has not been implemented")
40+
}
41+
42+
private let defaultTextAttributes: [NSAttributedString.Key: Any] = [
43+
.font: UIFont.systemFont(ofSize: 13),
44+
.foregroundColor: UIColor.ContentHeading.textColor,
45+
]
46+
47+
private let defaultLinkAttributes: [NSAttributedString.Key: Any] = [
48+
.font: UIFont.systemFont(ofSize: 13),
49+
.foregroundColor: UIColor.ContentHeading.linkColor,
50+
]
51+
52+
private func makeAttributedString() -> NSAttributedString {
53+
let body = NSLocalizedString(
54+
"IP_OVERRIDE_HEADER_BODY",
55+
tableName: "IPOverride",
56+
value: "Import files or text with new IP addresses for the servers in the Select location view.",
57+
comment: ""
58+
)
59+
let link = NSLocalizedString(
60+
"IP_OVERRIDE_HEADER_LINK",
61+
tableName: "IPOverride",
62+
value: "About IP override...",
63+
comment: ""
64+
)
65+
66+
var linkAttributes = defaultLinkAttributes
67+
linkAttributes[.link] = "#"
68+
69+
let paragraphStyle = NSMutableParagraphStyle()
70+
paragraphStyle.lineBreakMode = .byWordWrapping
71+
72+
let attributedString = NSMutableAttributedString()
73+
attributedString.append(NSAttributedString(string: body, attributes: defaultTextAttributes))
74+
attributedString.append(NSAttributedString(string: " ", attributes: defaultTextAttributes))
75+
attributedString.append(NSAttributedString(string: link, attributes: linkAttributes))
76+
attributedString.addAttribute(
77+
.paragraphStyle,
78+
value: paragraphStyle,
79+
range: NSRange(location: 0, length: attributedString.length)
80+
)
81+
return attributedString
82+
}
83+
84+
private func addSubviews() {
85+
addConstrainedSubviews([textView]) {
86+
textView.pinEdgesToSuperviewMargins()
87+
}
88+
}
89+
90+
func textView(
91+
_ textView: UITextView,
92+
shouldInteractWith URL: URL,
93+
in characterRange: NSRange,
94+
interaction: UITextItemInteraction
95+
) -> Bool {
96+
onAbout?()
97+
return false
98+
}
99+
100+
@available(iOS 17.0, *)
101+
func textView(_ textView: UITextView, menuConfigurationFor textItem: UITextItem, defaultMenu: UIMenu) -> UITextItem
102+
.MenuConfiguration? {
103+
return nil
104+
}
105+
106+
@available(iOS 17.0, *)
107+
func textView(_ textView: UITextView, primaryActionFor textItem: UITextItem, defaultAction: UIAction) -> UIAction? {
108+
if case .link = textItem.content {
109+
return UIAction { [weak self] _ in
110+
self?.onAbout?()
111+
}
112+
}
113+
return nil
114+
}
115+
}

Diff for: ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewController.swift

+11-71
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class IPOverrideViewController: UIViewController {
1313
private let interactor: IPOverrideInteractor
1414
private var cancellables = Set<AnyCancellable>()
1515
private let alertPresenter: AlertPresenter
16+
private let headerView = IPOverrideHeaderView()
1617

1718
weak var delegate: IPOverrideViewControllerDelegate?
1819

@@ -51,11 +52,11 @@ class IPOverrideViewController: UIViewController {
5152
override func viewDidLoad() {
5253
super.viewDidLoad()
5354

54-
navigationController?.navigationItem.largeTitleDisplayMode = .never
5555
view.backgroundColor = .secondaryColor
56+
view.directionalLayoutMargins = UIMetrics.contentHeadingLayoutMargins
5657

57-
addHeader()
58-
addPreamble()
58+
configureNavigation()
59+
addHeaderView()
5960
addImportButtons()
6061
addStatusLabel()
6162

@@ -70,44 +71,21 @@ class IPOverrideViewController: UIViewController {
7071
}.store(in: &cancellables)
7172
}
7273

73-
private func addHeader() {
74-
let label = UILabel()
75-
label.font = .systemFont(ofSize: 32, weight: .bold)
76-
label.textColor = .white
77-
label.text = NSLocalizedString(
74+
private func configureNavigation() {
75+
title = NSLocalizedString(
7876
"IP_OVERRIDE_HEADER",
7977
tableName: "IPOverride",
8078
value: "Server IP override",
8179
comment: ""
8280
)
83-
84-
let infoButton = UIButton(type: .custom)
85-
infoButton.tintColor = .white
86-
infoButton.setImage(UIImage(resource: .iconInfo), for: .normal)
87-
infoButton.addTarget(self, action: #selector(didTapInfoButton), for: .touchUpInside)
88-
infoButton.heightAnchor.constraint(equalToConstant: 24).isActive = true
89-
infoButton.widthAnchor.constraint(equalTo: infoButton.heightAnchor, multiplier: 1).isActive = true
90-
91-
let headerView = UIStackView(arrangedSubviews: [label, infoButton, UIView()])
92-
headerView.spacing = 8
93-
94-
containerView.addArrangedSubview(headerView)
95-
containerView.setCustomSpacing(14, after: headerView)
9681
}
9782

98-
private func addPreamble() {
99-
let label = UILabel()
100-
label.font = .systemFont(ofSize: 12, weight: .semibold)
101-
label.textColor = .white.withAlphaComponent(0.6)
102-
label.numberOfLines = 0
103-
label.text = NSLocalizedString(
104-
"IP_OVERRIDE_PREAMBLE",
105-
tableName: "IPOverride",
106-
value: "Import files or text with new IP addresses for the servers in the Select location view.",
107-
comment: ""
108-
)
83+
private func addHeaderView() {
84+
headerView.onAbout = { [weak self] in
85+
self?.delegate?.presentAbout()
86+
}
10987

110-
containerView.addArrangedSubview(label)
88+
containerView.addArrangedSubview(headerView)
11189
}
11290

11391
private func addImportButtons() {
@@ -140,44 +118,6 @@ class IPOverrideViewController: UIViewController {
140118
containerView.addArrangedSubview(statusView)
141119
}
142120

143-
@objc private func didTapInfoButton() {
144-
let message = NSLocalizedString(
145-
"IP_OVERRIDE_DIALOG_MESSAGE",
146-
tableName: "IPOverride",
147-
value: """
148-
On some networks, where various types of censorship are being used, our server IP addresses are \
149-
sometimes blocked.
150-
To circumvent this you can import a file or a text, provided by our support team, \
151-
with new IP addresses that override the default addresses of the servers in the Select location view.
152-
If you are having issues connecting to VPN servers, please contact support.
153-
""",
154-
comment: ""
155-
)
156-
157-
let presentation = AlertPresentation(
158-
id: "ip-override-info-alert",
159-
icon: .info,
160-
title: NSLocalizedString(
161-
"IP_OVERRIDE_INFO_DIALOG_TITLE",
162-
tableName: "IPOverride",
163-
value: "Server IP override",
164-
comment: ""
165-
),
166-
message: message,
167-
buttons: [AlertAction(
168-
title: NSLocalizedString(
169-
"IP_OVERRIDE_INFO_DIALOG_OK_BUTTON",
170-
tableName: "IPOverride",
171-
value: "Got it!",
172-
comment: ""
173-
),
174-
style: .default
175-
)]
176-
)
177-
178-
alertPresenter.showAlert(presentation: presentation, animated: true)
179-
}
180-
181121
@objc private func didTapClearButton() {
182122
let presentation = AlertPresentation(
183123
id: "ip-override-clear-alert",

Diff for: ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideViewControllerDelegate.swift

+1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ import Foundation
1010

1111
protocol IPOverrideViewControllerDelegate: AnyObject {
1212
func presentImportTextController()
13+
func presentAbout()
1314
}

0 commit comments

Comments
 (0)