diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2019-12-10 11:15:31 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2019-12-10 11:15:31 +0100 |
| commit | 2aacf8fb21ff13905a338bd373aa365adec0ae7c (patch) | |
| tree | 05f13f79a233186e7db92176f0afbb75e771864c | |
| parent | 87eeab54df529f6129da48eba32fbbc4c196649d (diff) | |
| parent | a75d471cfea6068c6ff059b4a75e328796e94621 (diff) | |
| download | mullvadvpn-2aacf8fb21ff13905a338bd373aa365adec0ae7c.tar.xz mullvadvpn-2aacf8fb21ff13905a338bd373aa365adec0ae7c.zip | |
Merge branch 'wireup-connect-controller-ios'
| -rw-r--r-- | ios/MullvadVPN/Base.lproj/Main.storyboard | 395 | ||||
| -rw-r--r-- | ios/MullvadVPN/ConnectViewController.swift | 138 | ||||
| -rw-r--r-- | ios/MullvadVPN/CustomButton.swift | 4 | ||||
| -rw-r--r-- | ios/MullvadVPN/RootContainerViewController.swift | 24 | ||||
| -rw-r--r-- | ios/MullvadVPN/SegueIdentifier.swift | 5 | ||||
| -rw-r--r-- | ios/MullvadVPN/TunnelControlViewController.swift | 96 | ||||
| -rw-r--r-- | ios/MullvadVPN/UIColor+Palette.swift | 34 |
7 files changed, 580 insertions, 116 deletions
diff --git a/ios/MullvadVPN/Base.lproj/Main.storyboard b/ios/MullvadVPN/Base.lproj/Main.storyboard index 118282aaa9..389973eab7 100644 --- a/ios/MullvadVPN/Base.lproj/Main.storyboard +++ b/ios/MullvadVPN/Base.lproj/Main.storyboard @@ -39,19 +39,19 @@ <rect key="frame" x="0.0" y="0.0" width="375" height="93"/> <subviews> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="LogoIcon" translatesAutoresizingMaskIntoConstraints="NO" id="cKg-hE-JsS"> - <rect key="frame" x="12" y="48.5" width="16" height="16"/> + <rect key="frame" x="12" y="6.5" width="98" height="100"/> </imageView> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uXv-Tf-PET"> - <rect key="frame" x="343" y="45.5" width="16" height="22"/> + <rect key="frame" x="311" y="32.5" width="48" height="48"/> <state key="normal" image="IconSettings"/> <connections> <segue destination="Kqv-qu-mfF" kind="presentation" identifier="ShowSettings" id="cxu-NC-VeP"/> </connections> </button> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="MULLVAD VPN" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dqy-A0-TdV"> - <rect key="frame" x="36" y="42" width="168" height="29"/> + <rect key="frame" x="118" y="42" width="168" height="29"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="24"/> - <color key="textColor" white="1" alpha="0.80" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <color key="textColor" white="1" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <nil key="highlightedColor"/> </label> </subviews> @@ -100,10 +100,10 @@ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <subviews> <view contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0ZY-Kh-JiM" userLabel="Container"> - <rect key="frame" x="0.0" y="20" width="375" height="647"/> + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <subviews> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pID-oa-Rrg" customClass="SpinnerActivityIndicatorView" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="163.5" y="162.5" width="48" height="48"/> + <rect key="frame" x="163.5" y="172.5" width="48" height="48"/> <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <constraints> <constraint firstAttribute="height" constant="48" id="2J4-Qc-ctc"/> @@ -111,10 +111,10 @@ </constraints> </view> <imageView clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.0" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="IconSuccess" translatesAutoresizingMaskIntoConstraints="NO" id="7ux-Tb-Fzq"> - <rect key="frame" x="157.5" y="156.5" width="60" height="60"/> + <rect key="frame" x="157.5" y="166.5" width="60" height="60"/> </imageView> <view contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" translatesAutoresizingMaskIntoConstraints="NO" id="V3j-Lb-fSQ" userLabel="Form"> - <rect key="frame" x="0.0" y="240.5" width="375" height="126"/> + <rect key="frame" x="0.0" y="250.5" width="375" height="126"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Login" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Nxn-Fc-EGe"> <rect key="frame" x="24" y="0.0" width="327" height="39"/> @@ -133,7 +133,6 @@ <subviews> <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="0000 0000 0000 0000" textAlignment="natural" adjustsFontSizeToFit="NO" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="XOB-ct-yLU" userLabel="Account Text Field" customClass="AccountTextField" customModule="MullvadVPN" customModuleProvider="target"> <rect key="frame" x="0.0" y="0.0" width="327" height="48"/> - <nil key="textColor"/> <fontDescription key="fontDescription" type="system" pointSize="20"/> <textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="numberPad" enablesReturnKeyAutomatically="YES" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no" textContentType="username"/> <connections> @@ -270,98 +269,81 @@ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> - <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="MapBackground" translatesAutoresizingMaskIntoConstraints="NO" id="3Ck-JT-ogd"> - <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> - </imageView> - <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="shY-Lj-oYx"> - <rect key="frame" x="24" y="543" width="327" height="100"/> + <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="3rI-k6-N1S"> + <rect key="frame" x="24" y="304" width="327" height="59"/> <subviews> - <visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JHO-Ca-Zzd" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="327" height="42"/> - <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="jVH-JP-pJo"> - <rect key="frame" x="0.0" y="0.0" width="327" height="42"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hVz-q0-Xpd" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="327" height="42"/> - <fontDescription key="fontDescription" type="system" pointSize="20"/> - <inset key="contentEdgeInsets" minX="0.0" minY="0.0" maxX="0.0" maxY="10"/> - <state key="normal" title="Select location" backgroundImage="TranslucentNeutralButton"> - <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - </state> - <connections> - <segue destination="hOC-Ab-N3D" kind="presentation" id="LlL-R9-hNI"/> - </connections> - </button> - </subviews> - <constraints> - <constraint firstAttribute="bottom" secondItem="hVz-q0-Xpd" secondAttribute="bottom" id="4Q2-Zh-7fM"/> - <constraint firstAttribute="trailing" secondItem="hVz-q0-Xpd" secondAttribute="trailing" id="AsY-cg-1fj"/> - <constraint firstItem="hVz-q0-Xpd" firstAttribute="leading" secondItem="jVH-JP-pJo" secondAttribute="leading" id="Eya-ig-8On"/> - <constraint firstItem="hVz-q0-Xpd" firstAttribute="top" secondItem="jVH-JP-pJo" secondAttribute="top" id="UY2-bK-i8s"/> - </constraints> - </view> - <constraints> - <constraint firstAttribute="height" constant="42" id="Doo-X5-rCc"/> - </constraints> - <blurEffect style="light"/> - </visualEffectView> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vxU-Mt-fMo" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="58" width="327" height="42"/> - <constraints> - <constraint firstAttribute="height" constant="42" placeholder="YES" id="C9s-Bs-hA5"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="20"/> - <state key="normal" title="Secure my connection" backgroundImage="SuccessButton"> - <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - </state> - </button> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SECURE CONNECTION" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HNy-mU-nui"> + <rect key="frame" x="0.0" y="0.0" width="327" height="26.5"/> + <fontDescription key="fontDescription" type="system" pointSize="22"/> + <color key="textColor" red="0.26666666666666666" green="0.67843137254901964" blue="0.30196078431372547" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sweden" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9Yf-sl-l3q"> + <rect key="frame" x="0.0" y="32.5" width="327" height="26.5"/> + <fontDescription key="fontDescription" type="system" pointSize="22"/> + <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <nil key="highlightedColor"/> + </label> </subviews> </stackView> + <containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Dv1-XA-XQG" userLabel="Tunnel control buttons"> + <rect key="frame" x="24" y="481" width="327" height="162"/> + <constraints> + <constraint firstAttribute="height" constant="162" placeholder="YES" id="kLh-W4-WzX"/> + </constraints> + <connections> + <segue destination="7We-1v-DIF" kind="embed" identifier="EmbedTunnelControls" id="SUp-a7-wNJ"/> + </connections> + </containerView> </subviews> <color key="backgroundColor" red="0.16078431369999999" green="0.30196078430000001" blue="0.45098039220000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <constraints> - <constraint firstAttribute="trailingMargin" secondItem="shY-Lj-oYx" secondAttribute="trailing" id="2y6-PR-9e3"/> - <constraint firstItem="3Ck-JT-ogd" firstAttribute="top" secondItem="PNd-mm-N1B" secondAttribute="top" id="EBE-j1-lqr"/> - <constraint firstItem="3Ck-JT-ogd" firstAttribute="leading" secondItem="PNd-mm-N1B" secondAttribute="leading" id="OEK-r4-gE7"/> - <constraint firstAttribute="bottom" secondItem="3Ck-JT-ogd" secondAttribute="bottom" id="e75-bI-sYJ"/> - <constraint firstAttribute="trailing" secondItem="3Ck-JT-ogd" secondAttribute="trailing" id="fXi-Cn-1bF"/> - <constraint firstAttribute="bottomMargin" secondItem="shY-Lj-oYx" secondAttribute="bottom" id="rYu-al-UqH"/> - <constraint firstItem="shY-Lj-oYx" firstAttribute="leading" secondItem="PNd-mm-N1B" secondAttribute="leadingMargin" id="v5s-FD-Iaw"/> + <constraint firstItem="3rI-k6-N1S" firstAttribute="leading" secondItem="PNd-mm-N1B" secondAttribute="leadingMargin" id="5fs-z1-QKf"/> + <constraint firstItem="3rI-k6-N1S" firstAttribute="centerY" secondItem="PNd-mm-N1B" secondAttribute="centerY" id="MU7-g1-1QL"/> + <constraint firstAttribute="trailingMargin" secondItem="3rI-k6-N1S" secondAttribute="trailing" id="NFr-rI-7r3"/> + <constraint firstItem="iBo-pG-OTz" firstAttribute="trailing" secondItem="Dv1-XA-XQG" secondAttribute="trailing" constant="24" id="OPf-p4-dPn"/> + <constraint firstItem="iBo-pG-OTz" firstAttribute="bottom" secondItem="Dv1-XA-XQG" secondAttribute="bottom" constant="24" id="sjJ-M6-O74"/> + <constraint firstItem="Dv1-XA-XQG" firstAttribute="leading" secondItem="iBo-pG-OTz" secondAttribute="leading" constant="24" id="ye6-tj-a0l"/> </constraints> <edgeInsets key="layoutMargins" top="0.0" left="24" bottom="24" right="24"/> <viewLayoutGuide key="safeArea" id="iBo-pG-OTz"/> </view> + <connections> + <outlet property="countryLabel" destination="9Yf-sl-l3q" id="TZY-gM-Qr8"/> + <outlet property="secureLabel" destination="HNy-mU-nui" id="QBg-mR-Z6g"/> + <segue destination="hOC-Ab-N3D" kind="presentation" identifier="ShowRelaySelector" id="mui-V1-CK4"/> + </connections> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="gkg-dm-hcG" userLabel="First Responder" sceneMemberID="firstResponder"/> </objects> - <point key="canvasLocation" x="1690" y="27"/> + <point key="canvasLocation" x="1688.8" y="26.53673163418291"/> </scene> <!--Settings--> <scene sceneID="3oF-uu-3Bk"> <objects> <tableViewController id="SHd-a4-ewi" customClass="SettingsViewController" customModule="MullvadVPN" customModuleProvider="target" sceneMemberID="viewController"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="6Gz-UM-orK"> - <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <rect key="frame" x="0.0" y="0.0" width="375" height="647"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="backgroundColor" red="0.098039215690000001" green="0.18039215689999999" blue="0.27058823529999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="separatorColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <prototypes> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Account" id="ghE-jC-RWf" customClass="SettingsAccountCell" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="55.5" width="375" height="44"/> + <rect key="frame" x="0.0" y="55.5" width="375" height="43"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ghE-jC-RWf" id="sTl-gI-g2a"> - <rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/> + <rect key="frame" x="0.0" y="0.0" width="348" height="43"/> <autoresizingMask key="autoresizingMask"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Account" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Lve-Kd-qTr"> - <rect key="frame" x="16" y="11" width="63.5" height="22"/> + <rect key="frame" x="16" y="11" width="63.5" height="21"/> <fontDescription key="fontDescription" type="system" pointSize="17"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <nil key="highlightedColor"/> </label> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" text="A YEAR LEFT" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QeD-EQ-Ruo"> - <rect key="frame" x="252" y="11" width="81" height="22"/> + <rect key="frame" x="259" y="11" width="81" height="21"/> <fontDescription key="fontDescription" type="system" weight="medium" pointSize="13"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <nil key="highlightedColor"/> @@ -385,20 +367,20 @@ </connections> </tableViewCell> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="AppVersion" id="pbd-iC-Emm" customClass="SettingsAppVersionCell" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="99.5" width="375" height="44"/> + <rect key="frame" x="0.0" y="98.5" width="375" height="43"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="pbd-iC-Emm" id="lYp-Z8-1sN"> - <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/> + <rect key="frame" x="0.0" y="0.0" width="375" height="43"/> <autoresizingMask key="autoresizingMask"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="App version" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pYC-Zb-8N9"> - <rect key="frame" x="16" y="11" width="91" height="22"/> + <rect key="frame" x="16" y="11" width="91" height="21"/> <fontDescription key="fontDescription" type="system" pointSize="17"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <nil key="highlightedColor"/> </label> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" text="2018.3" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sOr-vj-cg7"> - <rect key="frame" x="316.5" y="11" width="42.5" height="22"/> + <rect key="frame" x="316.5" y="11" width="42.5" height="21"/> <fontDescription key="fontDescription" type="system" weight="medium" pointSize="13"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <nil key="highlightedColor"/> @@ -451,16 +433,16 @@ <objects> <viewController id="ruh-Q2-P39" customClass="AccountViewController" customModule="MullvadVPN" customModuleProvider="target" sceneMemberID="viewController"> <view key="view" contentMode="scaleToFill" id="Qpl-bL-ZGl"> - <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <rect key="frame" x="0.0" y="0.0" width="375" height="647"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> - <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" alwaysBounceVertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="saE-dV-AgF"> - <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="saE-dV-AgF"> + <rect key="frame" x="0.0" y="0.0" width="375" height="647"/> <subviews> - <view contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rkG-Xa-pEO" userLabel="Container"> - <rect key="frame" x="0.0" y="0.0" width="375" height="279"/> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rkG-Xa-pEO" userLabel="Container"> + <rect key="frame" x="0.0" y="0.0" width="375" height="295"/> <subviews> - <view contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nkx-Eb-7le" userLabel="Content"> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="nkx-Eb-7le" userLabel="Content"> <rect key="frame" x="24" y="24" width="327" height="247"/> <subviews> <view contentMode="scaleToFill" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="HzF-8Z-UBs" userLabel="Account number"> @@ -525,7 +507,7 @@ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="24" translatesAutoresizingMaskIntoConstraints="NO" id="wNk-FP-mVD"> <rect key="frame" x="0.0" y="0.0" width="327" height="108"/> <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2Ia-Dz-pE6" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target"> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2Ia-Dz-pE6" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target"> <rect key="frame" x="0.0" y="0.0" width="327" height="42"/> <constraints> <constraint firstAttribute="height" constant="42" placeholder="YES" id="9Ss-ab-obH"/> @@ -536,7 +518,7 @@ <action selector="doBuyCredits" destination="ruh-Q2-P39" eventType="touchUpInside" id="BAj-cb-ENq"/> </connections> </button> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QHr-Lz-v6t" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target"> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QHr-Lz-v6t" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target"> <rect key="frame" x="0.0" y="66" width="327" height="42"/> <constraints> <constraint firstAttribute="height" constant="42" placeholder="YES" id="VYx-GQ-CIz"/> @@ -574,10 +556,10 @@ </view> </subviews> <constraints> - <constraint firstAttribute="bottomMargin" secondItem="nkx-Eb-7le" secondAttribute="bottom" id="28V-SW-noS"/> - <constraint firstAttribute="trailingMargin" secondItem="nkx-Eb-7le" secondAttribute="trailing" id="KQB-PO-stg"/> - <constraint firstItem="nkx-Eb-7le" firstAttribute="leading" secondItem="rkG-Xa-pEO" secondAttribute="leadingMargin" id="L4C-cS-yzC"/> - <constraint firstItem="nkx-Eb-7le" firstAttribute="top" secondItem="rkG-Xa-pEO" secondAttribute="topMargin" id="eea-1e-zMf"/> + <constraint firstAttribute="bottom" secondItem="nkx-Eb-7le" secondAttribute="bottom" constant="24" id="28V-SW-noS"/> + <constraint firstAttribute="trailing" secondItem="nkx-Eb-7le" secondAttribute="trailing" constant="24" id="KQB-PO-stg"/> + <constraint firstItem="nkx-Eb-7le" firstAttribute="leading" secondItem="rkG-Xa-pEO" secondAttribute="leading" constant="24" id="L4C-cS-yzC"/> + <constraint firstItem="nkx-Eb-7le" firstAttribute="top" secondItem="rkG-Xa-pEO" secondAttribute="top" constant="24" id="eea-1e-zMf"/> </constraints> </view> </subviews> @@ -597,7 +579,6 @@ <constraint firstItem="saE-dV-AgF" firstAttribute="top" secondItem="Qpl-bL-ZGl" secondAttribute="top" id="sZ0-CC-Onn"/> <constraint firstItem="saE-dV-AgF" firstAttribute="leading" secondItem="Qpl-bL-ZGl" secondAttribute="leading" id="xba-Jt-Ulk"/> </constraints> - <edgeInsets key="layoutMargins" top="24" left="24" bottom="24" right="24"/> <viewLayoutGuide key="safeArea" id="jrJ-di-3DV"/> </view> <navigationItem key="navigationItem" title="Account" id="rL3-Y8-3g8"/> @@ -616,8 +597,9 @@ <scene sceneID="er3-W2-NkS"> <objects> <navigationController id="Kqv-qu-mfF" sceneMemberID="viewController"> + <navigationItem key="navigationItem" id="7IR-NQ-qLb"/> <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" barStyle="black" largeTitles="YES" id="7PK-0x-byW" customClass="CustomNavigationBar" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="20" width="375" height="96"/> + <rect key="frame" x="0.0" y="0.0" width="375" height="108"/> <autoresizingMask key="autoresizingMask"/> <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> </navigationBar> @@ -634,7 +616,7 @@ <objects> <navigationController id="hOC-Ab-N3D" sceneMemberID="viewController"> <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" barStyle="black" largeTitles="YES" id="kmu-Ab-x1c" customClass="CustomNavigationBar" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="20" width="375" height="96"/> + <rect key="frame" x="0.0" y="0.0" width="375" height="108"/> <autoresizingMask key="autoresizingMask"/> <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <textAttributes key="largeTitleTextAttributes"> @@ -654,7 +636,7 @@ <objects> <tableViewController 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"/> + <rect key="frame" x="0.0" y="0.0" width="375" height="647"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="backgroundColor" red="0.098039215690000001" green="0.18039215689999999" blue="0.27058823529999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="separatorColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> @@ -663,7 +645,7 @@ <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="44" width="327" height="77"/> + <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"/> @@ -680,7 +662,7 @@ </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="44"/> + <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"/> @@ -694,7 +676,7 @@ </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="22"/> + <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"/> @@ -748,14 +730,245 @@ </barButtonItem> </navigationItem> <connections> + <outlet property="activityIndicator" destination="Mdw-ey-b8l" id="w7b-yv-Q39"/> <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"/> + <view contentMode="scaleToFill" id="Mdw-ey-b8l" customClass="SpinnerActivityIndicatorView" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="48" height="48"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <viewLayoutGuide key="safeArea" id="gNQ-Ao-v6S"/> + </view> <exit id="6Lc-ZQ-E4P" userLabel="Exit" sceneMemberID="exit"/> </objects> <point key="canvasLocation" x="2649" y="841"/> </scene> + <!--Tunnel Control View Controller--> + <scene sceneID="Zzn-S1-fVu"> + <objects> + <viewController id="7We-1v-DIF" customClass="TunnelControlViewController" customModule="MullvadVPN" customModuleProvider="target" sceneMemberID="viewController"> + <view key="view" contentMode="scaleToFill" id="Ayf-ZB-cCJ"> + <rect key="frame" x="0.0" y="0.0" width="327" height="162"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <viewLayoutGuide key="safeArea" id="MPh-Rn-8Ad"/> + </view> + <connections> + <outlet property="connectedView" destination="KX2-Pi-Myg" id="qSH-w0-rkV"/> + <outlet property="connectingView" destination="ZYQ-G2-MXc" id="Hg7-Sg-PFl"/> + <outlet property="disconnectedView" destination="7l6-on-fbR" id="UT0-9I-faY"/> + </connections> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="C3V-lo-QFw" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> + <view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="7l6-on-fbR"> + <rect key="frame" x="0.0" y="0.0" width="261" height="121"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <subviews> + <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="shY-Lj-oYx"> + <rect key="frame" x="0.0" y="0.0" width="261" height="121"/> + <subviews> + <visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JHO-Ca-Zzd" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52.5"/> + <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="jVH-JP-pJo"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52.5"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hVz-q0-Xpd" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52.5"/> + <inset key="contentEdgeInsets" minX="0.0" minY="0.0" maxX="0.0" maxY="10"/> + <state key="normal" title="Select location" backgroundImage="TranslucentNeutralButton"> + <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </state> + <connections> + <action selector="handleSelectLocation:" destination="7We-1v-DIF" eventType="touchUpInside" id="P4j-WA-6kP"/> + </connections> + </button> + </subviews> + <constraints> + <constraint firstAttribute="bottom" secondItem="hVz-q0-Xpd" secondAttribute="bottom" id="4Q2-Zh-7fM"/> + <constraint firstAttribute="trailing" secondItem="hVz-q0-Xpd" secondAttribute="trailing" id="AsY-cg-1fj"/> + <constraint firstItem="hVz-q0-Xpd" firstAttribute="leading" secondItem="jVH-JP-pJo" secondAttribute="leading" id="Eya-ig-8On"/> + <constraint firstItem="hVz-q0-Xpd" firstAttribute="top" secondItem="jVH-JP-pJo" secondAttribute="top" id="UY2-bK-i8s"/> + </constraints> + </view> + <blurEffect style="light"/> + </visualEffectView> + <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vxU-Mt-fMo" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="68.5" width="261" height="52.5"/> + <state key="normal" title="Secure my connection" backgroundImage="SuccessButton"> + <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </state> + <connections> + <action selector="handleSecureConnection:" destination="7We-1v-DIF" eventType="touchUpInside" id="XmB-3v-mds"/> + </connections> + </button> + </subviews> + </stackView> + </subviews> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <constraints> + <constraint firstItem="shY-Lj-oYx" firstAttribute="leading" secondItem="7l6-on-fbR" secondAttribute="leading" id="1th-7a-npG"/> + <constraint firstItem="shY-Lj-oYx" firstAttribute="bottom" secondItem="7l6-on-fbR" secondAttribute="bottom" id="53Y-II-Op2"/> + <constraint firstAttribute="trailing" secondItem="shY-Lj-oYx" secondAttribute="trailing" id="6QT-NE-4OX"/> + <constraint firstItem="shY-Lj-oYx" firstAttribute="trailing" secondItem="7l6-on-fbR" secondAttribute="trailing" id="ORr-CI-hQE"/> + <constraint firstItem="shY-Lj-oYx" firstAttribute="leading" secondItem="7l6-on-fbR" secondAttribute="leading" id="U5c-eT-w73"/> + <constraint firstItem="shY-Lj-oYx" firstAttribute="top" secondItem="7l6-on-fbR" secondAttribute="top" id="cea-vh-q3j"/> + <constraint firstAttribute="bottom" secondItem="shY-Lj-oYx" secondAttribute="bottom" id="isW-KM-CjB"/> + <constraint firstItem="shY-Lj-oYx" firstAttribute="top" secondItem="7l6-on-fbR" secondAttribute="top" id="zkC-qT-d72"/> + </constraints> + </view> + <view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="ZYQ-G2-MXc"> + <rect key="frame" x="0.0" y="0.0" width="261" height="120"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <subviews> + <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="K2E-tQ-f7f"> + <rect key="frame" x="0.0" y="0.0" width="261" height="120"/> + <subviews> + <visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="HGl-4o-WUZ" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> + <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="tbu-8S-1Wk"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dbp-iY-d0K" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> + <inset key="contentEdgeInsets" minX="0.0" minY="0.0" maxX="0.0" maxY="10"/> + <state key="normal" title="Switch location" backgroundImage="TranslucentNeutralButton"> + <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </state> + <connections> + <action selector="handleSelectLocation:" destination="7We-1v-DIF" eventType="touchUpInside" id="8dc-Ta-6qx"/> + </connections> + </button> + </subviews> + <constraints> + <constraint firstAttribute="bottom" secondItem="dbp-iY-d0K" secondAttribute="bottom" id="5LU-aU-B47"/> + <constraint firstAttribute="trailing" secondItem="dbp-iY-d0K" secondAttribute="trailing" id="7PF-WL-1iS"/> + <constraint firstItem="dbp-iY-d0K" firstAttribute="leading" secondItem="tbu-8S-1Wk" secondAttribute="leading" id="8h9-Dh-uGH"/> + <constraint firstItem="dbp-iY-d0K" firstAttribute="top" secondItem="tbu-8S-1Wk" secondAttribute="top" id="Xop-Vl-s9E"/> + </constraints> + </view> + <blurEffect style="light"/> + </visualEffectView> + <visualEffectView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" translatesAutoresizingMaskIntoConstraints="NO" id="wTZ-S5-pHI" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="68" width="261" height="52"/> + <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="hWm-jl-c2l"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PDP-HS-RB2" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> + <state key="normal" title="Cancel" backgroundImage="TranslucentDangerButton"> + <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </state> + <connections> + <action selector="handleDisconnect:" destination="7We-1v-DIF" eventType="touchUpInside" id="lgN-mu-5IE"/> + </connections> + </button> + </subviews> + <constraints> + <constraint firstItem="PDP-HS-RB2" firstAttribute="leading" secondItem="hWm-jl-c2l" secondAttribute="leading" id="0iK-cH-2T7"/> + <constraint firstItem="PDP-HS-RB2" firstAttribute="top" secondItem="hWm-jl-c2l" secondAttribute="top" id="Q9r-wY-cgh"/> + <constraint firstAttribute="bottom" secondItem="PDP-HS-RB2" secondAttribute="bottom" id="Rvj-jF-cwH"/> + <constraint firstAttribute="trailing" secondItem="PDP-HS-RB2" secondAttribute="trailing" id="Thh-Vt-L4v"/> + </constraints> + </view> + <blurEffect style="light"/> + </visualEffectView> + </subviews> + </stackView> + </subviews> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <constraints> + <constraint firstItem="K2E-tQ-f7f" firstAttribute="trailing" secondItem="ZYQ-G2-MXc" secondAttribute="trailing" id="8jj-NR-Sva"/> + <constraint firstItem="K2E-tQ-f7f" firstAttribute="bottom" secondItem="ZYQ-G2-MXc" secondAttribute="bottom" id="eza-7A-uNf"/> + <constraint firstItem="K2E-tQ-f7f" firstAttribute="top" secondItem="ZYQ-G2-MXc" secondAttribute="top" id="ndG-qN-nvg"/> + <constraint firstItem="K2E-tQ-f7f" firstAttribute="leading" secondItem="ZYQ-G2-MXc" secondAttribute="leading" id="phY-um-kkn"/> + <constraint firstAttribute="bottom" secondItem="K2E-tQ-f7f" secondAttribute="bottom" id="qUI-AH-MDO"/> + <constraint firstItem="K2E-tQ-f7f" firstAttribute="top" secondItem="ZYQ-G2-MXc" secondAttribute="top" id="s3u-gd-29a"/> + <constraint firstAttribute="trailing" secondItem="K2E-tQ-f7f" secondAttribute="trailing" id="x5Q-Uy-6xz"/> + <constraint firstItem="K2E-tQ-f7f" firstAttribute="leading" secondItem="ZYQ-G2-MXc" secondAttribute="leading" id="xkx-qv-Dcr"/> + </constraints> + </view> + <view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="KX2-Pi-Myg"> + <rect key="frame" x="0.0" y="0.0" width="261" height="120"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <subviews> + <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="J70-p3-IaC"> + <rect key="frame" x="0.0" y="0.0" width="261" height="120"/> + <subviews> + <visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fgw-rj-lK4" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> + <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="dSR-qU-xgH"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZbQ-zA-ZS8" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> + <inset key="contentEdgeInsets" minX="0.0" minY="0.0" maxX="0.0" maxY="10"/> + <state key="normal" title="Switch location" backgroundImage="TranslucentNeutralButton"> + <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </state> + <connections> + <action selector="handleSelectLocation:" destination="7We-1v-DIF" eventType="touchUpInside" id="rZA-gs-2O6"/> + </connections> + </button> + </subviews> + <constraints> + <constraint firstItem="ZbQ-zA-ZS8" firstAttribute="leading" secondItem="dSR-qU-xgH" secondAttribute="leading" id="c2Y-VY-hME"/> + <constraint firstAttribute="bottom" secondItem="ZbQ-zA-ZS8" secondAttribute="bottom" id="eUE-C6-e9C"/> + <constraint firstAttribute="trailing" secondItem="ZbQ-zA-ZS8" secondAttribute="trailing" id="ggD-w4-oQQ"/> + <constraint firstItem="ZbQ-zA-ZS8" firstAttribute="top" secondItem="dSR-qU-xgH" secondAttribute="top" id="oMx-RW-wv9"/> + </constraints> + </view> + <blurEffect style="light"/> + </visualEffectView> + <visualEffectView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" translatesAutoresizingMaskIntoConstraints="NO" id="sAx-Or-5Yn" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="68" width="261" height="52"/> + <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="ANR-Gb-KsI"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="d5t-ia-qxF" customClass="CustomButton" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> + <state key="normal" title="Disconnect" backgroundImage="TranslucentDangerButton"> + <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </state> + <connections> + <action selector="handleDisconnect:" destination="7We-1v-DIF" eventType="touchUpInside" id="S6r-fB-gnj"/> + </connections> + </button> + </subviews> + <constraints> + <constraint firstAttribute="trailing" secondItem="d5t-ia-qxF" secondAttribute="trailing" id="6AH-65-3Vh"/> + <constraint firstAttribute="bottom" secondItem="d5t-ia-qxF" secondAttribute="bottom" id="7aQ-oK-5Yo"/> + <constraint firstItem="d5t-ia-qxF" firstAttribute="leading" secondItem="ANR-Gb-KsI" secondAttribute="leading" id="EKV-6Y-UtV"/> + <constraint firstItem="d5t-ia-qxF" firstAttribute="top" secondItem="ANR-Gb-KsI" secondAttribute="top" id="NKf-WN-rVa"/> + </constraints> + </view> + <blurEffect style="light"/> + </visualEffectView> + </subviews> + </stackView> + </subviews> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <constraints> + <constraint firstItem="J70-p3-IaC" firstAttribute="bottom" secondItem="KX2-Pi-Myg" secondAttribute="bottom" id="0FU-IO-L7p"/> + <constraint firstItem="J70-p3-IaC" firstAttribute="top" secondItem="KX2-Pi-Myg" secondAttribute="top" id="KZn-7H-iDI"/> + <constraint firstItem="J70-p3-IaC" firstAttribute="top" secondItem="KX2-Pi-Myg" secondAttribute="top" id="NnJ-v1-dtl"/> + <constraint firstItem="J70-p3-IaC" firstAttribute="leading" secondItem="KX2-Pi-Myg" secondAttribute="leading" id="ZWy-eg-o1m"/> + <constraint firstAttribute="trailing" secondItem="J70-p3-IaC" secondAttribute="trailing" id="cFk-ve-P9q"/> + <constraint firstItem="J70-p3-IaC" firstAttribute="leading" secondItem="KX2-Pi-Myg" secondAttribute="leading" id="kjo-BJ-Odb"/> + <constraint firstItem="J70-p3-IaC" firstAttribute="trailing" secondItem="KX2-Pi-Myg" secondAttribute="trailing" id="saI-Tt-ucW"/> + <constraint firstAttribute="bottom" secondItem="J70-p3-IaC" secondAttribute="bottom" id="tBe-df-ho5"/> + </constraints> + </view> + </objects> + <point key="canvasLocation" x="2541" y="78"/> + </scene> </scenes> <resources> <image name="DangerButton" width="9" height="9"/> @@ -766,8 +979,8 @@ <image name="IconSuccess" width="60" height="60"/> <image name="IconTick" width="24" height="24"/> <image name="LogoIcon" width="49" height="50"/> - <image name="MapBackground" width="637" height="982"/> <image name="SuccessButton" width="9" height="9"/> + <image name="TranslucentDangerButton" width="9" height="9"/> <image name="TranslucentNeutralButton" width="9" height="9"/> </resources> </document> diff --git a/ios/MullvadVPN/ConnectViewController.swift b/ios/MullvadVPN/ConnectViewController.swift index d7b615b3fb..8e9c4f09b7 100644 --- a/ios/MullvadVPN/ConnectViewController.swift +++ b/ios/MullvadVPN/ConnectViewController.swift @@ -6,27 +6,159 @@ // Copyright © 2019 Amagicom AB. All rights reserved. // +import Combine import UIKit +import NetworkExtension +import os -class ConnectViewController: UIViewController, RootContainment { +class ConnectViewController: UIViewController, RootContainment, TunnelControlViewControllerDelegate { + + @IBOutlet var secureLabel: UILabel! + @IBOutlet var countryLabel: UILabel! + + private var setRelaysSubscriber: AnyCancellable? + private var startStopTunnelSubscriber: AnyCancellable? + private var tunnelStateSubscriber: AnyCancellable? override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent } var preferredHeaderBarStyle: HeaderBarStyle { - return .unsecured + switch tunnelState { + case .connecting, .reconnecting, .connected: + return .secured + + case .disconnecting, .disconnected: + return .unsecured + } + } + + private var tunnelState: TunnelState = .disconnected { + didSet { + setNeedsHeaderBarStyleAppearanceUpdate() + updateSecureLabel() + updateTunnelConnectionInfo() + } } override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view, typically from a nib. + + tunnelStateSubscriber = TunnelManager.shared.$tunnelState + .receive(on: DispatchQueue.main) + .assign(to: \.tunnelState, on: self) + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if case .embedTunnelControls = SegueIdentifier.Connect.from(segue: segue) { + let tunnelControlController = segue.destination as! TunnelControlViewController + tunnelControlController.view.translatesAutoresizingMaskIntoConstraints = false + tunnelControlController.delegate = self + } + } + + // MARK: - TunnelControlViewControllerDelegate + + func tunnelControlViewController(_ controller: TunnelControlViewController, handleAction action: TunnelControlAction) { + switch action { + case .connect: + connectTunnel() + + case .disconnect: + disconnectTunnel() + + case .selectLocation: + performSegue(withIdentifier: SegueIdentifier.Connect.showRelaySelector.rawValue, sender: self) + } + } + + // MARK: - Private + + private func updateSecureLabel() { + secureLabel.text = tunnelState.textForSecureLabel() + secureLabel.textColor = tunnelState.textColorForSecureLabel() + } + + private func updateTunnelConnectionInfo() { + switch tunnelState { + case .connected(let connectionInfo), + .reconnecting(let connectionInfo): + countryLabel.text = "\(connectionInfo.hostname)\nIn: \(connectionInfo.ipv4Relay)" + + case .connecting, .disconnected, .disconnecting: + countryLabel.text = "" + } + } + + private func connectTunnel() { + startStopTunnelSubscriber = TunnelManager.shared.startTunnel() + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { (completion) in + if case .failure(let error) = completion { + os_log(.error, "Failed to start the tunnel: %{public}s", error.localizedDescription) + } + }) + } + + private func disconnectTunnel() { + startStopTunnelSubscriber = TunnelManager.shared.stopTunnel() + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { (_) in + // no-op + }) } // MARK: - Actions @IBAction func unwindFromSelectLocation(segue: UIStoryboardSegue) { + guard let selectLocationController = segue.source as? SelectLocationController else { return } + guard let selectedItem = selectLocationController.selectedItem else { return } + + let relayConstraints = RelayConstraints(location: .only(selectedItem.relayLocation)) + + setRelaysSubscriber = TunnelManager.shared.setRelayConstraints(relayConstraints) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { (completion) in + switch completion { + case .finished: + os_log(.debug, "Updated relay constraints: %{public}s", String(reflecting: relayConstraints)) + self.connectTunnel() + + case .failure(let error): + os_log(.error, "Failed to update relay constraints: %{public}s", error.localizedDescription) + } + }) + } + +} + +private extension TunnelState { + + func textColorForSecureLabel() -> UIColor { + switch self { + case .connecting, .reconnecting: + return .white + + case .connected: + return .successColor + + case .disconnecting, .disconnected: + return .dangerColor + } + } + + func textForSecureLabel() -> String { + switch self { + case .connecting, .reconnecting: + return NSLocalizedString("Creating secure connection", tableName: "Connect", comment: "") + + case .connected: + return NSLocalizedString("Secure connection", tableName: "Connect", comment: "") + case .disconnecting, .disconnected: + return NSLocalizedString("Unsecured connection", tableName: "Connect", comment: "") + } } } diff --git a/ios/MullvadVPN/CustomButton.swift b/ios/MullvadVPN/CustomButton.swift index 119403c91c..fa4c867cc9 100644 --- a/ios/MullvadVPN/CustomButton.swift +++ b/ios/MullvadVPN/CustomButton.swift @@ -43,7 +43,9 @@ import UIKit contentEdgeInsets = contentInsets titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold) - titleLabel?.textColor = UIColor.white + + setTitleColor(UIColor.white, for: .normal) + setTitleColor(UIColor.lightGray, for: .highlighted) } override func imageRect(forContentRect contentRect: CGRect) -> CGRect { diff --git a/ios/MullvadVPN/RootContainerViewController.swift b/ios/MullvadVPN/RootContainerViewController.swift index 7d1990e507..0ca4588232 100644 --- a/ios/MullvadVPN/RootContainerViewController.swift +++ b/ios/MullvadVPN/RootContainerViewController.swift @@ -114,6 +114,9 @@ class RootContainerViewController: UIViewController { // MARK: - Storyboard segue handling override func unwind(for unwindSegue: UIStoryboardSegue, towards subsequentVC: UIViewController) { + // Make sure there is no attempt to unwind to the top view controller + guard subsequentVC != topViewController else { return } + let index = viewControllers.firstIndex(of: subsequentVC)! let newViewControllers = Array(viewControllers.prefix(through: index)) @@ -246,7 +249,7 @@ class RootContainerViewController: UIViewController { } /// Request the root container to query the top controller for the new header bar style - func setNeedsHeaderBarStyleAppearance() { + func updateHeaderBarAppearance() { updateHeaderBarStyleFromChildPreferences(animated: UIView.areAnimationsEnabled) } @@ -324,20 +327,21 @@ class RootContainerPushSegue: UIStoryboardSegue { } } +/// A UIViewController extension that gives view controllers an access to root container extension UIViewController { var rootContainerController: RootContainerViewController? { - var viewController: UIViewController? = parent - - while viewController != nil { - if let container = viewController as? RootContainerViewController { - return container - } - - viewController = viewController?.parent + var current: UIViewController? = self + let iterator = AnyIterator { () -> UIViewController? in + current = current?.parent + return current } - return nil + return iterator.first { $0 is RootContainerViewController } as? RootContainerViewController + } + + func setNeedsHeaderBarStyleAppearanceUpdate() { + rootContainerController?.updateHeaderBarAppearance() } } diff --git a/ios/MullvadVPN/SegueIdentifier.swift b/ios/MullvadVPN/SegueIdentifier.swift index f1d9c0c71b..eaa8a10cbb 100644 --- a/ios/MullvadVPN/SegueIdentifier.swift +++ b/ios/MullvadVPN/SegueIdentifier.swift @@ -21,6 +21,11 @@ extension SegueIdentifier { case showConnect = "ShowConnect" } + enum Connect: String, SegueConvertible { + case embedTunnelControls = "EmbedTunnelControls" + case showRelaySelector = "ShowRelaySelector" + } + enum SelectLocation: String, SegueConvertible { case returnToConnectWithNewRelay = "ReturnToConnectWithNewRelay" } diff --git a/ios/MullvadVPN/TunnelControlViewController.swift b/ios/MullvadVPN/TunnelControlViewController.swift new file mode 100644 index 0000000000..2f9bc0eae4 --- /dev/null +++ b/ios/MullvadVPN/TunnelControlViewController.swift @@ -0,0 +1,96 @@ +// +// TunnelControlView.swift +// MullvadVPN +// +// Created by pronebird on 01/11/2019. +// Copyright © 2019 Amagicom AB. All rights reserved. +// + +import Combine +import UIKit + +enum TunnelControlAction { + /// An action emitted only when the tunnel is down + case connect + + /// An action emitted when user either selects to cancel the connection or disconnect when + /// the tunnel is already connected + case disconnect + + /// An action emitted when user requests to either select the location when the tunnel is down + /// or change the location when the tunnel is connecting or connected. + case selectLocation +} + +protocol TunnelControlViewControllerDelegate: class { + func tunnelControlViewController(_ controller: TunnelControlViewController, handleAction action: TunnelControlAction) -> Void +} + +class TunnelControlViewController: UIViewController { + + @IBOutlet var disconnectedView: UIView! + @IBOutlet var connectingView: UIView! + @IBOutlet var connectedView: UIView! + + weak var delegate: TunnelControlViewControllerDelegate? + + private var tunnelStateSubscriber: AnyCancellable? + private var controlsView: UIView? + + override func viewDidLoad() { + super.viewDidLoad() + + tunnelStateSubscriber = TunnelManager.shared.$tunnelState + .receive(on: DispatchQueue.main) + .sink { [weak self] (tunnelState) in + self?.didReceiveTunnelState(tunnelState) + } + } + + private func didReceiveTunnelState(_ tunnelState: TunnelState) { + switch tunnelState { + case .disconnected: + addControlsView(disconnectedView) + + case .connecting: + addControlsView(connectingView) + + case .connected, .reconnecting, .disconnecting: + addControlsView(connectedView) + } + } + + + private func addControlsView(_ nextControlsView: UIView) { + guard controlsView != nextControlsView else { return } + + controlsView?.removeFromSuperview() + controlsView = nextControlsView + + nextControlsView.translatesAutoresizingMaskIntoConstraints = false + + view.addSubview(nextControlsView) + + NSLayoutConstraint.activate([ + nextControlsView.topAnchor.constraint(equalTo: view.topAnchor), + nextControlsView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + nextControlsView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + nextControlsView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + ]) + } + + // MARK: - Actions + + @IBAction func handleSecureConnection(_ sender: Any) { + delegate?.tunnelControlViewController(self, handleAction: .connect) + } + + @IBAction func handleDisconnect(_ sender: Any) { + delegate?.tunnelControlViewController(self, handleAction: .disconnect) + } + + @IBAction func handleSelectLocation(_ sender: Any) { + delegate?.tunnelControlViewController(self, handleAction: .selectLocation) + } + +} diff --git a/ios/MullvadVPN/UIColor+Palette.swift b/ios/MullvadVPN/UIColor+Palette.swift index 72a1b4ace8..0298502fef 100644 --- a/ios/MullvadVPN/UIColor+Palette.swift +++ b/ios/MullvadVPN/UIColor+Palette.swift @@ -10,20 +10,20 @@ import UIKit extension UIColor { - struct AccountTextField { - struct NormalState { + enum AccountTextField { + enum NormalState { static let borderColor = UIColor(red: 0.10, green: 0.18, blue: 0.27, alpha: 1.0) static let textColor = UIColor(red: 0.16, green: 0.30, blue: 0.45, alpha: 1.0) static let backgroundColor = UIColor.white } - struct ErrorState { + enum ErrorState { static let borderColor = dangerColor.withAlphaComponent(0.4) static let textColor = dangerColor static let backgroundColor = UIColor.white } - struct AuthenticatingState { + enum AuthenticatingState { static let borderColor = UIColor.clear static let textColor = UIColor.white static let backgroundColor = UIColor.white.withAlphaComponent(0.2) @@ -31,27 +31,39 @@ extension UIColor { } // Relay availability indicator view - struct RelayStatusIndicator { + enum RelayStatusIndicator { static let activeColor = successColor.withAlphaComponent(0.9) static let inactiveColor = dangerColor.withAlphaComponent(0.95) } // Cells - struct Cell { + enum Cell { static let backgroundColor = UIColor(red: 0.16, green: 0.30, blue: 0.45, alpha: 1.0) - static let selectedAltBackgroundColor = backgroundColor.darkened(by: 0.2) + static let disabledBackgroundColor = backgroundColor.darkened(by: 0.3)! + static let selectedBackgroundColor = successColor - static let subCellBackgroundColor = UIColor(red: 0.15, green: 0.23, blue: 0.33, alpha: 1.0) - static let subSubCellBackgroundColor = UIColor(red: 0.13, green: 0.20, blue: 0.30, alpha: 1.0) + static let disabledSelectedBackgroundColor = selectedBackgroundColor.darkened(by: 0.3)! + + static let selectedAltBackgroundColor = backgroundColor.darkened(by: 0.2)! + } + + enum SubCell { + static let backgroundColor = UIColor(red: 0.15, green: 0.23, blue: 0.33, alpha: 1.0) + static let disabledBackgroundColor = backgroundColor.darkened(by: 0.3)! + } + + enum SubSubCell { + static let backgroundColor = UIColor(red: 0.13, green: 0.20, blue: 0.30, alpha: 1.0) + static let disabledBackgroundColor = backgroundColor.darkened(by: 0.3)! } - struct HeaderBar { + enum HeaderBar { static let defaultBackgroundColor = UIColor(red: 0.16, green: 0.30, blue: 0.45, alpha: 1.0) static let unsecuredBackgroundColor = dangerColor static let securedBackgroundColor = successColor } // Common colors - static let dangerColor = UIColor(red: 0.82, green: 0.01, blue: 0.11, alpha: 1.0) + static let dangerColor = UIColor(red: 0.89, green: 0.25, blue: 0.22, alpha: 1.0) static let successColor = UIColor(red: 0.27, green: 0.68, blue: 0.30, alpha: 1.0) } |
