summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2020-07-23 15:17:46 +0300
committerAndrej Mihajlov <and@mullvad.net>2020-07-23 15:17:46 +0300
commitb46227b839724acad1ef587fee2949e55a54c74d (patch)
tree47edf1fb7799856eacdb7eacdfaebfa73c012c7a
parent5a3fc686918ed7386be143651363a331bfae6ece (diff)
parent64947bb7bca2dfd549fa074322432294f5768804 (diff)
downloadmullvadvpn-b46227b839724acad1ef587fee2949e55a54c74d.tar.xz
mullvadvpn-b46227b839724acad1ef587fee2949e55a54c74d.zip
Merge branch 'move-select-location-from-storyboard'
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj8
-rw-r--r--ios/MullvadVPN/Base.lproj/Main.storyboard110
-rw-r--r--ios/MullvadVPN/ConnectViewController.swift63
-rw-r--r--ios/MullvadVPN/DisplayChainedError.swift25
-rw-r--r--ios/MullvadVPN/SegueIdentifier.swift4
-rw-r--r--ios/MullvadVPN/SelectLocationCell.swift102
-rw-r--r--ios/MullvadVPN/SelectLocationController.swift16
-rw-r--r--ios/MullvadVPN/SelectLocationHeaderView.swift41
-rw-r--r--ios/MullvadVPN/SelectLocationNavigationController.swift63
-rw-r--r--ios/MullvadVPN/UIColor+Palette.swift2
10 files changed, 228 insertions, 206 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index bb12fd7909..38fda942ac 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -76,6 +76,8 @@
5857F23D24C8449A00CF6F47 /* TransformOperationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580EE22024B3240100F9D8A1 /* TransformOperationObserver.swift */; };
5857F23E24C844A000CF6F47 /* OperationBlockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580EE21424B3231200F9D8A1 /* OperationBlockObserver.swift */; };
5857F23F24C844AD00CF6F47 /* Locking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BA692D23E99EFF009DC256 /* Locking.swift */; };
+ 5857F24324C8662600CF6F47 /* SelectLocationHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5857F24224C8662600CF6F47 /* SelectLocationHeaderView.swift */; };
+ 5857F24724C882D700CF6F47 /* SelectLocationNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5857F24624C882D700CF6F47 /* SelectLocationNavigationController.swift */; };
5860F1C223A785C600CEA666 /* WireguardDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5860F1C123A785C600CEA666 /* WireguardDevice.swift */; };
5860F1C423A8D25F00CEA666 /* WireguardConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5860F1C323A8D25F00CEA666 /* WireguardConfiguration.swift */; };
5860F1EB23AA4CF300CEA666 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5860F1EA23AA4CF300CEA666 /* Logging.swift */; };
@@ -272,6 +274,8 @@
5845F841236CBACD00B2D93C /* PacketTunnelIpc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelIpc.swift; sourceTree = "<group>"; };
584B26F3237434D00073B10E /* RelaySelectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelaySelectorTests.swift; sourceTree = "<group>"; };
58561C98239A5D1500BD6B5E /* IPEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPEndpoint.swift; sourceTree = "<group>"; };
+ 5857F24224C8662600CF6F47 /* SelectLocationHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationHeaderView.swift; sourceTree = "<group>"; };
+ 5857F24624C882D700CF6F47 /* SelectLocationNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationNavigationController.swift; sourceTree = "<group>"; };
5860F1C123A785C600CEA666 /* WireguardDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireguardDevice.swift; sourceTree = "<group>"; };
5860F1C323A8D25F00CEA666 /* WireguardConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireguardConfiguration.swift; sourceTree = "<group>"; };
5860F1EA23AA4CF300CEA666 /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; };
@@ -541,6 +545,8 @@
5867A51B2248F26A005513C0 /* SegueIdentifier.swift */,
5888AD82227B11080051EB06 /* SelectLocationCell.swift */,
5888AD86227B17950051EB06 /* SelectLocationController.swift */,
+ 5857F24224C8662600CF6F47 /* SelectLocationHeaderView.swift */,
+ 5857F24624C882D700CF6F47 /* SelectLocationNavigationController.swift */,
582BB1B2229574F40055B6EF /* SettingsAccountCell.swift */,
581CBCEB2298041B00727D7F /* SettingsAppVersionCell.swift */,
5877152D23981C5B001F8237 /* SettingsBasicCell.swift */,
@@ -907,6 +913,7 @@
580EE21E24B3237F00F9D8A1 /* OutputOperation.swift in Sources */,
5840250122B1124600E4CFEC /* IpAddress+Codable.swift in Sources */,
58EC4E6C23915325003F5C5B /* Bundle+MullvadVersion.swift in Sources */,
+ 5857F24724C882D700CF6F47 /* SelectLocationNavigationController.swift in Sources */,
580EE21224B322FC00F9D8A1 /* ResultOperation.swift in Sources */,
58BA693123EADA6A009DC256 /* SimulatorTunnelProvider.swift in Sources */,
58E6771F24ADFE7800AA26E7 /* SettingsNavigationController.swift in Sources */,
@@ -968,6 +975,7 @@
581CBCEE229826FD00727D7F /* StaticTableViewDataSource.swift in Sources */,
58CE5E64224146200008646E /* AppDelegate.swift in Sources */,
58C6B35E22BBBFE3003C19AD /* Data+HexCoding.swift in Sources */,
+ 5857F24324C8662600CF6F47 /* SelectLocationHeaderView.swift in Sources */,
58AEEF652344A36000C9BBD5 /* KeychainError.swift in Sources */,
580EE22824B3289300F9D8A1 /* AssociatedValue.swift in Sources */,
58CCA01222424D11004F3011 /* SettingsViewController.swift in Sources */,
diff --git a/ios/MullvadVPN/Base.lproj/Main.storyboard b/ios/MullvadVPN/Base.lproj/Main.storyboard
index 164513734b..9cf7ac24e6 100644
--- a/ios/MullvadVPN/Base.lproj/Main.storyboard
+++ b/ios/MullvadVPN/Base.lproj/Main.storyboard
@@ -1011,114 +1011,6 @@
</objects>
<point key="canvasLocation" x="-551" y="27"/>
</scene>
- <!--Select location-->
- <scene sceneID="Kar-Ys-a6u">
- <objects>
- <tableViewController storyboardIdentifier="SelectLocation" id="FxZ-7F-3yi" customClass="SelectLocationController" customModule="MullvadVPN" customModuleProvider="target" sceneMemberID="viewController">
- <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="56" sectionHeaderHeight="28" sectionFooterHeight="28" id="LKX-4h-vIx">
- <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <color key="backgroundColor" name="Secondary"/>
- <color key="separatorColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <view key="tableHeaderView" contentMode="scaleToFill" id="YMi-O0-jT1">
- <rect key="frame" x="0.0" y="0.0" width="375" height="145"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <subviews>
- <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="While connected, your real location is masked with a private and secure location in the selected region" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="X0P-N8-lda">
- <rect key="frame" x="24" y="24" width="327" height="97"/>
- <fontDescription key="fontDescription" type="system" pointSize="17"/>
- <color key="textColor" white="1" alpha="0.60217786815068497" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <nil key="highlightedColor"/>
- </label>
- </subviews>
- <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <constraints>
- <constraint firstAttribute="bottomMargin" secondItem="X0P-N8-lda" secondAttribute="bottom" id="Ghh-mK-nAy"/>
- <constraint firstAttribute="trailingMargin" secondItem="X0P-N8-lda" secondAttribute="trailing" id="gRy-Wb-s8K"/>
- <constraint firstItem="X0P-N8-lda" firstAttribute="top" secondItem="YMi-O0-jT1" secondAttribute="topMargin" id="mHY-Fb-HcE"/>
- <constraint firstItem="X0P-N8-lda" firstAttribute="leading" secondItem="YMi-O0-jT1" secondAttribute="leadingMargin" id="s3I-Rw-1Jg"/>
- </constraints>
- <edgeInsets key="layoutMargins" top="24" left="24" bottom="24" right="24"/>
- </view>
- <prototypes>
- <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="aFz-H5-sPu" customClass="SelectLocationCell" customModule="MullvadVPN" customModuleProvider="target">
- <rect key="frame" x="0.0" y="173" width="375" height="43.5"/>
- <autoresizingMask key="autoresizingMask"/>
- <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="aFz-H5-sPu" id="6nQ-gT-vzf">
- <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5ag-N4-pUg" customClass="RelayStatusIndicatorView" customModule="MullvadVPN" customModuleProvider="target">
- <rect key="frame" x="16" y="14" width="16" height="16"/>
- <constraints>
- <constraint firstAttribute="height" constant="16" id="QWj-hh-I3P"/>
- <constraint firstAttribute="width" constant="16" id="TFV-yi-LXG"/>
- </constraints>
- </view>
- <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="y7o-0b-MUV">
- <rect key="frame" x="44" y="11" width="42" height="21.5"/>
- <fontDescription key="fontDescription" type="system" pointSize="17"/>
- <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <nil key="highlightedColor"/>
- </label>
- <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="IconTick" translatesAutoresizingMaskIntoConstraints="NO" id="e1o-Bl-zd5">
- <rect key="frame" x="12" y="10" width="24" height="24"/>
- <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- </imageView>
- <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KaW-bN-I51">
- <rect key="frame" x="311" y="0.0" width="64" height="43.5"/>
- <accessibility key="accessibilityConfiguration" identifier="ExpandButton"/>
- <constraints>
- <constraint firstAttribute="width" constant="64" id="UU3-Di-65E"/>
- </constraints>
- <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <state key="normal" image="IconChevronDown"/>
- </button>
- </subviews>
- <color key="backgroundColor" name="Primary"/>
- <constraints>
- <constraint firstItem="KaW-bN-I51" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="y7o-0b-MUV" secondAttribute="trailing" id="3uQ-T4-POk"/>
- <constraint firstAttribute="bottomMargin" secondItem="y7o-0b-MUV" secondAttribute="bottom" id="7ly-PI-8H3"/>
- <constraint firstAttribute="bottom" secondItem="KaW-bN-I51" secondAttribute="bottom" id="9I6-k7-21c"/>
- <constraint firstItem="e1o-Bl-zd5" firstAttribute="centerX" secondItem="5ag-N4-pUg" secondAttribute="centerX" id="Bk5-41-u3r"/>
- <constraint firstItem="e1o-Bl-zd5" firstAttribute="centerY" secondItem="5ag-N4-pUg" secondAttribute="centerY" id="Pfw-mx-SZ3"/>
- <constraint firstAttribute="trailing" secondItem="KaW-bN-I51" secondAttribute="trailing" id="Z2F-pa-wEE"/>
- <constraint firstItem="y7o-0b-MUV" firstAttribute="top" secondItem="6nQ-gT-vzf" secondAttribute="topMargin" id="dw6-el-6DC"/>
- <constraint firstItem="5ag-N4-pUg" firstAttribute="leading" secondItem="6nQ-gT-vzf" secondAttribute="leadingMargin" id="h2b-0z-IjZ"/>
- <constraint firstItem="KaW-bN-I51" firstAttribute="top" secondItem="6nQ-gT-vzf" secondAttribute="top" id="ong-F1-a4V"/>
- <constraint firstItem="5ag-N4-pUg" firstAttribute="centerY" secondItem="6nQ-gT-vzf" secondAttribute="centerY" id="upC-Vc-y0Y"/>
- <constraint firstItem="y7o-0b-MUV" firstAttribute="leading" secondItem="5ag-N4-pUg" secondAttribute="trailing" constant="12" id="ylV-VK-pUm"/>
- </constraints>
- </tableViewCellContentView>
- <connections>
- <outlet property="collapseButton" destination="KaW-bN-I51" id="n5C-yZ-39f"/>
- <outlet property="locationLabel" destination="y7o-0b-MUV" id="Pw4-Kb-uCu"/>
- <outlet property="statusIndicator" destination="5ag-N4-pUg" id="wXj-KL-JdB"/>
- <outlet property="tickImageView" destination="e1o-Bl-zd5" id="UjZ-ct-oPZ"/>
- </connections>
- </tableViewCell>
- </prototypes>
- <connections>
- <outlet property="delegate" destination="FxZ-7F-3yi" id="yWE-Dc-Wl5"/>
- </connections>
- </tableView>
- <navigationItem key="navigationItem" title="Select location" largeTitleDisplayMode="always" id="PZM-r8-1Sb">
- <barButtonItem key="rightBarButtonItem" style="done" systemItem="done" id="4T0-a3-Ce4">
- <connections>
- <segue destination="6Lc-ZQ-E4P" kind="unwind" identifier="" unwindAction="unwindFromSelectLocationWithSegue:" id="gAz-uu-Whd"/>
- </connections>
- </barButtonItem>
- </navigationItem>
- <simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
- <connections>
- <segue destination="6Lc-ZQ-E4P" kind="unwind" identifier="ReturnToConnectWithNewRelay" unwindAction="unwindFromSelectLocationWithSegue:" id="SPT-Ay-Cy3"/>
- </connections>
- </tableViewController>
- <placeholder placeholderIdentifier="IBFirstResponder" id="EvX-LH-gOg" userLabel="First Responder" sceneMemberID="firstResponder"/>
- <exit id="6Lc-ZQ-E4P" userLabel="Exit" sceneMemberID="exit"/>
- </objects>
- <point key="canvasLocation" x="1689" y="779"/>
- </scene>
<!--Tunnel Control View Controller-->
<scene sceneID="Zzn-S1-fVu">
<objects>
@@ -1352,10 +1244,8 @@
<resources>
<image name="DangerButton" width="9" height="9"/>
<image name="DefaultButton" width="9" height="9"/>
- <image name="IconChevronDown" width="24" height="24"/>
<image name="IconExtlink" width="16" height="16"/>
<image name="IconSuccess" width="60" height="60"/>
- <image name="IconTick" width="24" height="24"/>
<image name="LogoIcon" width="253" height="253"/>
<image name="SuccessButton" width="9" height="9"/>
<image name="TranslucentDangerButton" width="9" height="9"/>
diff --git a/ios/MullvadVPN/ConnectViewController.swift b/ios/MullvadVPN/ConnectViewController.swift
index ace0eb198b..52b8a87f8c 100644
--- a/ios/MullvadVPN/ConnectViewController.swift
+++ b/ios/MullvadVPN/ConnectViewController.swift
@@ -13,7 +13,8 @@ import os
class ConnectViewController: UIViewController,
RootContainment,
TunnelControlViewControllerDelegate,
- TunnelObserver
+ TunnelObserver,
+ SelectLocationDelegate
{
@IBOutlet var secureLabel: UILabel!
@@ -101,6 +102,31 @@ class ConnectViewController: UIViewController,
}
}
+ // MARK: - SelectLocationDelegate
+
+ func selectLocationController(_ controller: SelectLocationController, didSelectLocation location: RelayLocation) {
+ controller.dismiss(animated: true) {
+ let relayConstraints = RelayConstraints(location: .only(location))
+
+ TunnelManager.shared.setRelayConstraints(relayConstraints) { [weak self] (result) in
+ DispatchQueue.main.async {
+ switch result {
+ case .success:
+ os_log(.debug, "Updated relay constraints: %{public}s", "\(relayConstraints)")
+ self?.connectTunnel()
+
+ case .failure(let error):
+ os_log(.error, "Failed to update relay constraints: %{public}s", error.localizedDescription)
+ }
+ }
+ }
+ }
+ }
+
+ func selectLocationControllerDidCancel(_ controller: SelectLocationController) {
+ controller.dismiss(animated: true)
+ }
+
// MARK: - Private
private func updateSecureLabel() {
@@ -193,21 +219,14 @@ class ConnectViewController: UIViewController,
}
private func showSelectLocation() {
- let contentController = self.storyboard?.instantiateViewController(withIdentifier: ViewControllerIdentifier.selectLocation.rawValue) as! SelectLocationController
- contentController.navigationItem.title = NSLocalizedString("Select location", comment: "")
- contentController.navigationItem.largeTitleDisplayMode = .always
-
- let navController = UINavigationController(navigationBarClass: CustomNavigationBar.self, toolbarClass: nil)
- navController.viewControllers = [contentController]
- navController.navigationBar.prefersLargeTitles = true
- navController.navigationBar.barStyle = .black
- navController.navigationBar.tintColor = .white
+ let selectLocationController = SelectLocationNavigationController()
+ selectLocationController.selectLocationDelegate = self
// Disable root controller interaction
rootContainerController?.view.isUserInteractionEnabled = false
- contentController.prefetchData {
- self.present(navController, animated: true)
+ selectLocationController.prefetchData {
+ self.present(selectLocationController, animated: true)
// Re-enable root controller interaction
self.rootContainerController?.view.isUserInteractionEnabled = true
@@ -220,26 +239,6 @@ class ConnectViewController: UIViewController,
connectionPanel.toggleConnectionInfoVisibility()
}
- @IBAction func unwindFromSelectLocation(segue: UIStoryboardSegue) {
- guard let selectLocationController = segue.source as? SelectLocationController else { return }
- guard let selectedLocation = selectLocationController.selectedLocation else { return }
-
- let relayConstraints = RelayConstraints(location: .only(selectedLocation))
-
- TunnelManager.shared.setRelayConstraints(relayConstraints) { [weak self] (result) in
- DispatchQueue.main.async {
- switch result {
- case .success:
- os_log(.debug, "Updated relay constraints: %{public}s", "\(relayConstraints)")
- self?.connectTunnel()
-
- case .failure(let error):
- os_log(.error, "Failed to update relay constraints: %{public}s", error.localizedDescription)
- }
- }
- }
- }
-
}
private extension TunnelState {
diff --git a/ios/MullvadVPN/DisplayChainedError.swift b/ios/MullvadVPN/DisplayChainedError.swift
index 4fa2883e6b..8080d9d53b 100644
--- a/ios/MullvadVPN/DisplayChainedError.swift
+++ b/ios/MullvadVPN/DisplayChainedError.swift
@@ -130,7 +130,14 @@ extension AppStorePaymentManager.Error: DisplayChainedError {
return NSLocalizedString("Internal error: account is not set", comment: "")
case .readReceipt(let readReceiptError):
- return String(format: NSLocalizedString("Cannot read the receipt: %@", comment: ""), readReceiptError.errorChainDescription ?? "")
+ switch readReceiptError {
+ case .refresh(let storeError):
+ return String(format: NSLocalizedString("Cannot refresh the AppStore receipt: %@", comment: ""), storeError.localizedDescription)
+ case .io(let ioError):
+ return String(format: NSLocalizedString("Cannot read the AppStore receipt from disk: %@", comment: ""), ioError.localizedDescription)
+ case .doesNotExist:
+ return NSLocalizedString("AppStore receipt is not found on disk.", comment: "")
+ }
case .sendReceipt(let restError):
let reason = restError.errorChainDescription ?? ""
@@ -142,19 +149,3 @@ extension AppStorePaymentManager.Error: DisplayChainedError {
}
}
}
-
-extension AppStoreReceipt.Error: DisplayChainedError {
- var errorChainDescription: String? {
- switch self {
- case .doesNotExist:
- return NSLocalizedString("AppStore receipt does not exist", comment: "")
-
- case .io(let readError):
- return String(format: NSLocalizedString("Read error: %@", comment: ""),
- readError.localizedDescription)
-
- case .refresh(let refreshError):
- return String(format: NSLocalizedString("Failed to refresh the receipt: %@", comment: ""), refreshError.localizedDescription)
- }
- }
-}
diff --git a/ios/MullvadVPN/SegueIdentifier.swift b/ios/MullvadVPN/SegueIdentifier.swift
index d6345a4e06..3e9f2db97b 100644
--- a/ios/MullvadVPN/SegueIdentifier.swift
+++ b/ios/MullvadVPN/SegueIdentifier.swift
@@ -26,10 +26,6 @@ extension SegueIdentifier {
case embedTunnelControls = "EmbedTunnelControls"
}
- enum SelectLocation: String, SegueConvertible {
- case returnToConnectWithNewRelay = "ReturnToConnectWithNewRelay"
- }
-
enum Account: String, SegueConvertible {
case logout = "Logout"
}
diff --git a/ios/MullvadVPN/SelectLocationCell.swift b/ios/MullvadVPN/SelectLocationCell.swift
index 2500be383b..8d29a1d960 100644
--- a/ios/MullvadVPN/SelectLocationCell.swift
+++ b/ios/MullvadVPN/SelectLocationCell.swift
@@ -11,10 +11,10 @@ import UIKit
class SelectLocationCell: BasicTableViewCell {
typealias CollapseHandler = (SelectLocationCell) -> Void
- @IBOutlet var locationLabel: UILabel!
- @IBOutlet var statusIndicator: RelayStatusIndicatorView!
- @IBOutlet var tickImageView: UIImageView!
- @IBOutlet var collapseButton: UIButton!
+ let locationLabel = UILabel()
+ let statusIndicator = RelayStatusIndicatorView()
+ let tickImageView = UIImageView(image: UIImage(imageLiteralResourceName: "IconTick"))
+ let collapseButton = UIButton(type: .custom)
private let chevronDown = UIImage(imageLiteralResourceName: "IconChevronDown")
private let chevronUp = UIImage(imageLiteralResourceName: "IconChevronUp")
@@ -48,19 +48,14 @@ class SelectLocationCell: BasicTableViewCell {
}
}
- override func awakeFromNib() {
- super.awakeFromNib()
+ override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+ super.init(style: style, reuseIdentifier: reuseIdentifier)
- indentationWidth = 16
- statusIndicator.tintColor = .white
-
- collapseButton.addTarget(self, action: #selector(handleCollapseButton(_ :)), for: .touchUpInside)
-
- updateCollapseImage()
- updateDisabled()
- updateBackgroundColor()
+ setupCell()
+ }
- contentView.layoutMargins = preferredMargins
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
@@ -82,13 +77,61 @@ class SelectLocationCell: BasicTableViewCell {
updateTickImage()
}
+ private func setupCell() {
+ indentationWidth = 16
+
+ backgroundView = UIView()
+ selectedBackgroundView = UIView()
+ backgroundColor = .clear
+ contentView.layoutMargins = preferredMargins
+
+ locationLabel.font = UIFont.systemFont(ofSize: 17)
+ locationLabel.textColor = .white
+
+ statusIndicator.tintColor = .white
+ tickImageView.tintColor = .white
+
+ collapseButton.tintColor = .white
+ collapseButton.setImage(chevronDown, for: .normal)
+ collapseButton.addTarget(self, action: #selector(handleCollapseButton(_ :)), for: .touchUpInside)
+
+ [locationLabel, tickImageView, statusIndicator, collapseButton].forEach { (subview) in
+ subview.translatesAutoresizingMaskIntoConstraints = false
+ contentView.addSubview(subview)
+ }
+
+ updateCollapseImage()
+ updateDisabled()
+ updateBackgroundColor()
+
+ NSLayoutConstraint.activate([
+ tickImageView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
+ tickImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
+
+ statusIndicator.widthAnchor.constraint(equalToConstant: 16),
+ statusIndicator.heightAnchor.constraint(equalToConstant: 16),
+ statusIndicator.centerXAnchor.constraint(equalTo: tickImageView.centerXAnchor),
+ statusIndicator.centerYAnchor.constraint(equalTo: tickImageView.centerYAnchor),
+
+ locationLabel.leadingAnchor.constraint(equalTo: statusIndicator.trailingAnchor, constant: 12),
+ locationLabel.trailingAnchor.constraint(greaterThanOrEqualTo: collapseButton.leadingAnchor, constant: 0),
+ locationLabel.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor),
+ locationLabel.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor),
+
+ collapseButton.widthAnchor.constraint(equalToConstant: 70),
+ collapseButton.topAnchor.constraint(equalTo: contentView.topAnchor),
+ collapseButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
+ collapseButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
+ ])
+ }
+
private func updateTickImage() {
statusIndicator.isHidden = isSelected
- tickImageView?.isHidden = !isSelected
+ tickImageView.isHidden = !isSelected
}
private func updateDisabled() {
- contentView.alpha = isDisabled ? 0.5 : 1
+ locationLabel.alpha = isDisabled ? 0.2 : 1
}
private func updateBackgroundColor() {
@@ -97,24 +140,13 @@ class SelectLocationCell: BasicTableViewCell {
}
private func backgroundColorForIdentationLevel() -> UIColor {
- if isDisabled {
- switch indentationLevel {
- case 1:
- return UIColor.SubCell.disabledBackgroundColor
- case 2:
- return UIColor.SubSubCell.disabledBackgroundColor
- default:
- return UIColor.Cell.disabledBackgroundColor
- }
- } else {
- switch indentationLevel {
- case 1:
- return UIColor.SubCell.backgroundColor
- case 2:
- return UIColor.SubSubCell.backgroundColor
- default:
- return UIColor.Cell.backgroundColor
- }
+ switch indentationLevel {
+ case 1:
+ return UIColor.SubCell.backgroundColor
+ case 2:
+ return UIColor.SubSubCell.backgroundColor
+ default:
+ return UIColor.Cell.backgroundColor
}
}
diff --git a/ios/MullvadVPN/SelectLocationController.swift b/ios/MullvadVPN/SelectLocationController.swift
index affb127003..d996421b20 100644
--- a/ios/MullvadVPN/SelectLocationController.swift
+++ b/ios/MullvadVPN/SelectLocationController.swift
@@ -33,13 +33,19 @@ class SelectLocationController: UITableViewController, RelayCacheObserver {
private var expandedItems = [RelayLocation]()
private var dataSource: DataSource?
- var selectedLocation: RelayLocation?
+ var didSelectLocationHandler: ((RelayLocation) -> Void)?
// MARK: - View lifecycle
override func viewDidLoad() {
super.viewDidLoad()
+ view.backgroundColor = .secondaryColor
+ tableView.tableHeaderView = SelectLocationHeaderView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
+ tableView.register(SelectLocationCell.self, forCellReuseIdentifier: kCellIdentifier)
+ tableView.separatorColor = .secondaryColor
+ tableView.separatorInset = .zero
+
dataSource = DataSource(
tableView: self.tableView,
cellProvider: { [weak self] (tableView, indexPath, item) -> UITableViewCell? in
@@ -61,6 +67,7 @@ class SelectLocationController: UITableViewController, RelayCacheObserver {
return cell
})
+ dataSource?.defaultRowAnimation = .top
tableView.dataSource = dataSource
RelayCache.shared.addObserver(self)
@@ -89,14 +96,11 @@ class SelectLocationController: UITableViewController, RelayCacheObserver {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let item = dataSource?.itemIdentifier(for: indexPath) else { return }
- selectedLocation = item.relayLocation
-
- // Return back to the main view after selecting the relay
+ // Disable interaction with the controller after selection
tableView.isUserInteractionEnabled = false
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(250)) {
- self.performSegue(withIdentifier:
- SegueIdentifier.SelectLocation.returnToConnectWithNewRelay.rawValue, sender: self)
+ self.didSelectLocationHandler?(item.relayLocation)
}
}
diff --git a/ios/MullvadVPN/SelectLocationHeaderView.swift b/ios/MullvadVPN/SelectLocationHeaderView.swift
new file mode 100644
index 0000000000..d5b0268008
--- /dev/null
+++ b/ios/MullvadVPN/SelectLocationHeaderView.swift
@@ -0,0 +1,41 @@
+//
+// SelectLocationHeaderView.swift
+// MullvadVPN
+//
+// Created by pronebird on 22/07/2020.
+// Copyright © 2020 Mullvad VPN AB. All rights reserved.
+//
+
+import UIKit
+
+class SelectLocationHeaderView: UIView {
+
+ let textLabel = UILabel()
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+
+ layoutMargins = UIEdgeInsets(top: 24, left: 24, bottom: 24, right: 24)
+
+ textLabel.translatesAutoresizingMaskIntoConstraints = false
+ textLabel.font = UIFont.systemFont(ofSize: 17)
+ textLabel.textColor = UIColor(white: 1, alpha: 0.6)
+ textLabel.numberOfLines = 0
+ textLabel.text = NSLocalizedString("While connected, your real location is masked with a private and secure location in the selected region", comment: "")
+
+ addSubview(textLabel)
+
+ NSLayoutConstraint.activate([
+ textLabel.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
+ textLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
+ textLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
+ textLabel.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor),
+ textLabel.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
+ ])
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+}
diff --git a/ios/MullvadVPN/SelectLocationNavigationController.swift b/ios/MullvadVPN/SelectLocationNavigationController.swift
new file mode 100644
index 0000000000..08c3b3b7e8
--- /dev/null
+++ b/ios/MullvadVPN/SelectLocationNavigationController.swift
@@ -0,0 +1,63 @@
+//
+// SelectLocationNavigationController.swift
+// MullvadVPN
+//
+// Created by pronebird on 22/07/2020.
+// Copyright © 2020 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+protocol SelectLocationDelegate: class {
+ func selectLocationController(_ controller: SelectLocationController, didSelectLocation location: RelayLocation)
+ func selectLocationControllerDidCancel(_ controller: SelectLocationController)
+}
+
+class SelectLocationNavigationController: UINavigationController {
+ private weak var contentController: SelectLocationController?
+
+ weak var selectLocationDelegate: SelectLocationDelegate?
+
+ init() {
+ super.init(navigationBarClass: CustomNavigationBar.self, toolbarClass: nil)
+
+ navigationBar.prefersLargeTitles = true
+ navigationBar.barStyle = .black
+ navigationBar.tintColor = .white
+
+ let contentController = SelectLocationController()
+ contentController.navigationItem.title = NSLocalizedString("Select location", comment: "")
+ contentController.navigationItem.largeTitleDisplayMode = .always
+ contentController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleDone(_:)))
+
+ contentController.didSelectLocationHandler = { [weak self] (location) in
+ guard let self = self, let contentController = self.contentController else { return }
+
+ self.selectLocationDelegate?.selectLocationController(contentController, didSelectLocation: location)
+ }
+
+ self.contentController = contentController
+ self.viewControllers = [contentController]
+ }
+
+ override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
+ // This override has to exist to prevent crash on iOS 12 where `UINavigationController`
+ // calls `self.init(nibName:bundle:)` internally.
+ super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ func prefetchData(_ completionHandler: @escaping () -> Void) {
+ contentController?.prefetchData(completionHandler: completionHandler)
+ }
+
+ @objc func handleDone(_ sender: AnyObject) {
+ if let contentController = contentController {
+ selectLocationDelegate?.selectLocationControllerDidCancel(contentController)
+ }
+ }
+}
diff --git a/ios/MullvadVPN/UIColor+Palette.swift b/ios/MullvadVPN/UIColor+Palette.swift
index 8235f857a3..9f9e64f27f 100644
--- a/ios/MullvadVPN/UIColor+Palette.swift
+++ b/ios/MullvadVPN/UIColor+Palette.swift
@@ -55,12 +55,10 @@ extension UIColor {
enum SubCell {
static let backgroundColor = namedColor("SubCell")
- static let disabledBackgroundColor = backgroundColor.darkened(by: 0.3)!
}
enum SubSubCell {
static let backgroundColor = namedColor("SubSubCell")
- static let disabledBackgroundColor = backgroundColor.darkened(by: 0.3)!
}
enum HeaderBar {