mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-22 17:42:45 +02:00
Refactor DeviceVerification prefix classes to KeyVerification where needed. And move key verification related classes into the same module KeyVerification.
This commit is contained in:
+75
@@ -0,0 +1,75 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
final class UserVerificationSessionStatusCoordinator: UserVerificationSessionStatusCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let userId: String
|
||||
private let deviceId: String
|
||||
private var userVerificationSessionStatusViewModel: UserVerificationSessionStatusViewModelType
|
||||
private let userVerificationSessionStatusViewController: UserVerificationSessionStatusViewController
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: UserVerificationSessionStatusCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, userId: String, userDisplayName: String?, deviceId: String) {
|
||||
self.session = session
|
||||
self.userId = userId
|
||||
self.deviceId = deviceId
|
||||
|
||||
let userVerificationSessionStatusViewModel = UserVerificationSessionStatusViewModel(session: self.session, userId: userId, userDisplayName: userDisplayName, deviceId: deviceId)
|
||||
let userVerificationSessionStatusViewController = UserVerificationSessionStatusViewController.instantiate(with: userVerificationSessionStatusViewModel)
|
||||
self.userVerificationSessionStatusViewModel = userVerificationSessionStatusViewModel
|
||||
self.userVerificationSessionStatusViewController = userVerificationSessionStatusViewController
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
self.userVerificationSessionStatusViewModel.coordinatorDelegate = self
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.userVerificationSessionStatusViewController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationSessionStatusViewModelCoordinatorDelegate
|
||||
extension UserVerificationSessionStatusCoordinator: UserVerificationSessionStatusViewModelCoordinatorDelegate {
|
||||
|
||||
func userVerificationSessionStatusViewModel(_ viewModel: UserVerificationSessionStatusViewModelType, wantsToManuallyVerifyDeviceWithId deviceId: String, for userId: String) {
|
||||
self.delegate?.userVerificationSessionStatusCoordinator(self, wantsToManuallyVerifyDeviceWithId: deviceId, for: userId)
|
||||
}
|
||||
|
||||
func userVerificationSessionStatusViewModelDidClose(_ viewModel: UserVerificationSessionStatusViewModelType) {
|
||||
self.delegate?.userVerificationSessionStatusCoordinatorDidClose(self)
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol UserVerificationSessionStatusCoordinatorDelegate: class {
|
||||
func userVerificationSessionStatusCoordinator(_ coordinator: UserVerificationSessionStatusCoordinatorType, wantsToManuallyVerifyDeviceWithId deviceId: String, for userId: String)
|
||||
func userVerificationSessionStatusCoordinatorDidClose(_ coordinator: UserVerificationSessionStatusCoordinatorType)
|
||||
}
|
||||
|
||||
/// `UserVerificationSessionStatusCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
|
||||
protocol UserVerificationSessionStatusCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: UserVerificationSessionStatusCoordinatorDelegate? { get }
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// UserVerificationSessionStatusViewController view actions exposed to view model
|
||||
enum UserVerificationSessionStatusViewAction {
|
||||
case loadData
|
||||
case verify
|
||||
case close
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="edG-Ef-G5W">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--User Verification Session Status View Controller-->
|
||||
<scene sceneID="yMy-34-3pS">
|
||||
<objects>
|
||||
<viewController id="edG-Ef-G5W" customClass="UserVerificationSessionStatusViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="G1A-RF-WrC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="16P-bA-3wP">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sX2-Xu-9f5">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="296.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1lx-d3-6c2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="108"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Qdf-kG-wgx">
|
||||
<rect key="frame" x="325" y="20" width="30" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="Pon-L2-rt1"/>
|
||||
<constraint firstAttribute="width" constant="30" id="qav-Na-EDh"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<state key="normal" image="close_button"/>
|
||||
<connections>
|
||||
<action selector="closeButtonAction:" destination="edG-Ef-G5W" eventType="touchUpInside" id="0qe-Df-Nw1"/>
|
||||
</connections>
|
||||
</button>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="encryption_warning" translatesAutoresizingMaskIntoConstraints="NO" id="0d0-9z-5Ar">
|
||||
<rect key="frame" x="20" y="23" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="0d0-9z-5Ar" secondAttribute="height" multiplier="1:1" id="EYK-FL-Zx6"/>
|
||||
<constraint firstAttribute="height" constant="24" id="MuJ-Ex-1Vk"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Warning" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mXI-Sg-wYe">
|
||||
<rect key="frame" x="54" y="23" width="261" height="24"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Messages with this user in this room are end-to-end encrypted and can’t be read by third parties." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CTY-XM-iLf">
|
||||
<rect key="frame" x="20" y="62" width="335" height="36"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="Qdf-kG-wgx" secondAttribute="trailing" constant="20" id="2JS-ui-NNI"/>
|
||||
<constraint firstItem="Qdf-kG-wgx" firstAttribute="leading" secondItem="mXI-Sg-wYe" secondAttribute="trailing" constant="10" id="7VI-oy-V0w"/>
|
||||
<constraint firstItem="CTY-XM-iLf" firstAttribute="top" secondItem="mXI-Sg-wYe" secondAttribute="bottom" constant="15" id="8UG-sf-OQU"/>
|
||||
<constraint firstItem="mXI-Sg-wYe" firstAttribute="centerY" secondItem="Qdf-kG-wgx" secondAttribute="centerY" id="Ad5-Kd-ox8"/>
|
||||
<constraint firstItem="0d0-9z-5Ar" firstAttribute="centerY" secondItem="mXI-Sg-wYe" secondAttribute="centerY" id="Jlp-rC-N2E"/>
|
||||
<constraint firstItem="CTY-XM-iLf" firstAttribute="trailing" secondItem="Qdf-kG-wgx" secondAttribute="trailing" id="LK6-M8-kCT"/>
|
||||
<constraint firstAttribute="bottom" secondItem="CTY-XM-iLf" secondAttribute="bottom" constant="10" id="MrU-Ar-zkT"/>
|
||||
<constraint firstItem="mXI-Sg-wYe" firstAttribute="leading" secondItem="0d0-9z-5Ar" secondAttribute="trailing" constant="10" id="O4U-Wa-zdT"/>
|
||||
<constraint firstItem="CTY-XM-iLf" firstAttribute="leading" secondItem="0d0-9z-5Ar" secondAttribute="leading" id="UeO-oR-wzQ"/>
|
||||
<constraint firstItem="Qdf-kG-wgx" firstAttribute="top" secondItem="1lx-d3-6c2" secondAttribute="top" constant="20" id="WOL-w8-7T3"/>
|
||||
<constraint firstItem="0d0-9z-5Ar" firstAttribute="leading" secondItem="1lx-d3-6c2" secondAttribute="leading" constant="20" id="cMk-cs-f9i"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3Mu-7j-flw">
|
||||
<rect key="frame" x="0.0" y="108" width="375" height="40.5"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="encryption_warning" translatesAutoresizingMaskIntoConstraints="NO" id="0ME-4K-MWA" customClass="encryption_warning">
|
||||
<rect key="frame" x="20" y="13.5" width="14" height="14"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="0ME-4K-MWA" secondAttribute="height" multiplier="1:1" id="r0c-Ix-lm4"/>
|
||||
<constraint firstAttribute="width" constant="14" id="zKC-ZT-6y8"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Riot iOS (BLPOWKUPGQ)" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FET-ZT-Q69">
|
||||
<rect key="frame" x="44" y="10" width="311" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="0ME-4K-MWA" firstAttribute="leading" secondItem="3Mu-7j-flw" secondAttribute="leading" constant="20" id="8W6-Zu-Qu2"/>
|
||||
<constraint firstAttribute="trailing" secondItem="FET-ZT-Q69" secondAttribute="trailing" constant="20" id="BbB-ge-jNu"/>
|
||||
<constraint firstItem="FET-ZT-Q69" firstAttribute="top" secondItem="3Mu-7j-flw" secondAttribute="top" constant="10" id="Bw3-IL-AwO"/>
|
||||
<constraint firstAttribute="bottom" secondItem="FET-ZT-Q69" secondAttribute="bottom" constant="10" id="NGo-OD-AHt"/>
|
||||
<constraint firstItem="FET-ZT-Q69" firstAttribute="leading" secondItem="0ME-4K-MWA" secondAttribute="trailing" constant="10" id="Sap-sa-3tW"/>
|
||||
<constraint firstItem="0ME-4K-MWA" firstAttribute="centerY" secondItem="FET-ZT-Q69" secondAttribute="centerY" id="W4V-Dz-CKf"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="rB7-Ct-Mq7">
|
||||
<rect key="frame" x="0.0" y="148.5" width="375" height="148"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oRN-W6-TVF">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="148"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dd2-N5-GEx">
|
||||
<rect key="frame" x="20" y="10" width="335" height="54"/>
|
||||
<string key="text">Until this user trusts this device, messages sent to and from it are labelled with warnings. Alternatively, you can manually verify it.</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="HiI-uV-Hjw">
|
||||
<rect key="frame" x="20" y="84" width="335" height="44"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCLoginButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="LKd-e3-0AI"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="exF-8e-73K"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
|
||||
<inset key="contentEdgeInsets" minX="30" minY="0.0" maxX="30" maxY="0.0"/>
|
||||
<state key="normal" title="Manually verify">
|
||||
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="verifyButtonAction:" destination="edG-Ef-G5W" eventType="touchUpInside" id="gGx-OT-X5w"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="HiI-uV-Hjw" secondAttribute="bottom" constant="20" id="3bP-Hs-m42"/>
|
||||
<constraint firstAttribute="trailing" secondItem="dd2-N5-GEx" secondAttribute="trailing" constant="20" id="BHw-Yv-ceG"/>
|
||||
<constraint firstItem="dd2-N5-GEx" firstAttribute="leading" secondItem="oRN-W6-TVF" secondAttribute="leading" constant="20" id="BV7-Vu-PQR"/>
|
||||
<constraint firstItem="HiI-uV-Hjw" firstAttribute="top" secondItem="dd2-N5-GEx" secondAttribute="bottom" constant="20" id="BtT-Mg-Okq"/>
|
||||
<constraint firstItem="HiI-uV-Hjw" firstAttribute="centerX" secondItem="oRN-W6-TVF" secondAttribute="centerX" id="Ign-Yp-tpB"/>
|
||||
<constraint firstItem="dd2-N5-GEx" firstAttribute="top" secondItem="oRN-W6-TVF" secondAttribute="top" constant="10" id="Ygn-tl-ahK"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="HiI-uV-Hjw" secondAttribute="trailing" constant="20" id="nqA-lS-ubx"/>
|
||||
<constraint firstItem="HiI-uV-Hjw" firstAttribute="width" secondItem="oRN-W6-TVF" secondAttribute="width" priority="250" id="qp1-z5-eP9"/>
|
||||
<constraint firstItem="HiI-uV-Hjw" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="oRN-W6-TVF" secondAttribute="leading" constant="20" id="uNl-Ok-uee"/>
|
||||
<constraint firstAttribute="height" priority="250" id="yuI-ch-y92"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="oRN-W6-TVF" firstAttribute="width" secondItem="rB7-Ct-Mq7" secondAttribute="width" id="2pR-3D-RYU"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="3Mu-7j-flw" secondAttribute="trailing" id="W3d-d0-w2v"/>
|
||||
<constraint firstItem="rB7-Ct-Mq7" firstAttribute="leading" secondItem="sX2-Xu-9f5" secondAttribute="leading" id="aH4-6p-pgC"/>
|
||||
<constraint firstItem="1lx-d3-6c2" firstAttribute="top" secondItem="sX2-Xu-9f5" secondAttribute="top" id="aTB-di-K6F"/>
|
||||
<constraint firstAttribute="bottom" secondItem="rB7-Ct-Mq7" secondAttribute="bottom" id="dOS-zD-hAw"/>
|
||||
<constraint firstAttribute="trailing" secondItem="rB7-Ct-Mq7" secondAttribute="trailing" id="iH7-HJ-6RG"/>
|
||||
<constraint firstAttribute="trailing" secondItem="1lx-d3-6c2" secondAttribute="trailing" id="lLi-mF-pmE"/>
|
||||
<constraint firstItem="1lx-d3-6c2" firstAttribute="leading" secondItem="sX2-Xu-9f5" secondAttribute="leading" id="rU8-aC-b2j"/>
|
||||
<constraint firstItem="3Mu-7j-flw" firstAttribute="leading" secondItem="sX2-Xu-9f5" secondAttribute="leading" id="thK-xG-Pvw"/>
|
||||
<constraint firstItem="3Mu-7j-flw" firstAttribute="top" secondItem="1lx-d3-6c2" secondAttribute="bottom" id="xjU-4b-Ac0"/>
|
||||
<constraint firstItem="rB7-Ct-Mq7" firstAttribute="top" secondItem="3Mu-7j-flw" secondAttribute="bottom" id="yOc-gk-ilO"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="sX2-Xu-9f5" secondAttribute="trailing" id="9uF-gg-w2a"/>
|
||||
<constraint firstItem="sX2-Xu-9f5" firstAttribute="width" secondItem="16P-bA-3wP" secondAttribute="width" id="AbT-qq-n6g"/>
|
||||
<constraint firstItem="sX2-Xu-9f5" firstAttribute="leading" secondItem="16P-bA-3wP" secondAttribute="leading" id="pP0-dc-iec"/>
|
||||
<constraint firstAttribute="bottom" secondItem="sX2-Xu-9f5" secondAttribute="bottom" id="tUz-rr-k5B"/>
|
||||
<constraint firstItem="sX2-Xu-9f5" firstAttribute="top" secondItem="16P-bA-3wP" secondAttribute="top" id="yrj-Cm-1Zm"/>
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="16P-bA-3wP" secondAttribute="bottom" id="3Ak-zJ-1Ca"/>
|
||||
<constraint firstItem="16P-bA-3wP" firstAttribute="top" secondItem="cZY-v6-GjO" secondAttribute="top" id="SV3-Ai-4QS"/>
|
||||
<constraint firstItem="16P-bA-3wP" firstAttribute="leading" secondItem="cZY-v6-GjO" secondAttribute="leading" id="dlk-85-Zca"/>
|
||||
<constraint firstItem="16P-bA-3wP" firstAttribute="trailing" secondItem="cZY-v6-GjO" secondAttribute="trailing" id="hFM-5p-Ne1"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="cZY-v6-GjO"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="badgeImageView" destination="0d0-9z-5Ar" id="M0i-8Q-Zal"/>
|
||||
<outlet property="closeButton" destination="Qdf-kG-wgx" id="yl7-lg-gW6"/>
|
||||
<outlet property="deviceInformationLabel" destination="FET-ZT-Q69" id="D8n-5b-H4p"/>
|
||||
<outlet property="deviceStatusImageView" destination="0ME-4K-MWA" id="kgp-dT-Q0P"/>
|
||||
<outlet property="informationLabel" destination="CTY-XM-iLf" id="qVC-lS-YOY"/>
|
||||
<outlet property="titleLabel" destination="mXI-Sg-wYe" id="Wrj-QM-srt"/>
|
||||
<outlet property="untrustedSessionContainerView" destination="oRN-W6-TVF" id="9sc-53-MWY"/>
|
||||
<outlet property="untrustedSessionInformationLabel" destination="dd2-N5-GEx" id="6TB-Ve-OyC"/>
|
||||
<outlet property="verifyButton" destination="HiI-uV-Hjw" id="JnR-0Q-aSc"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="8Fe-S5-Pf4" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-175.19999999999999" y="124.58770614692655"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="close_button" width="16" height="16"/>
|
||||
<image name="encryption_warning" width="16" height="16"/>
|
||||
</resources>
|
||||
</document>
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
final class UserVerificationSessionStatusViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let verifyButtonCornerRadius: CGFloat = 8.0
|
||||
static let informationTextDefaultFont = UIFont.systemFont(ofSize: 15.0)
|
||||
static let informationTextBoldFont = UIFont.systemFont(ofSize: 15.0, weight: .medium)
|
||||
static let deviceNameFont = UIFont.systemFont(ofSize: 17.0, weight: .medium)
|
||||
static let deviceIdFont = UIFont.systemFont(ofSize: 15.0)
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var badgeImageView: UIImageView!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var closeButton: UIButton!
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var deviceStatusImageView: UIImageView!
|
||||
@IBOutlet private weak var deviceInformationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var untrustedSessionContainerView: UIView!
|
||||
@IBOutlet private weak var untrustedSessionInformationLabel: UILabel!
|
||||
@IBOutlet private weak var verifyButton: UIButton!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: UserVerificationSessionStatusViewModelType!
|
||||
private var theme: Theme!
|
||||
private var keyboardAvoider: KeyboardAvoider?
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: UserVerificationSessionStatusViewModelType) -> UserVerificationSessionStatusViewController {
|
||||
let viewController = StoryboardScene.UserVerificationSessionStatusViewController.initialScene.instantiate()
|
||||
viewController.viewModel = viewModel
|
||||
viewController.theme = ThemeService.shared().theme
|
||||
return viewController
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.setupViews()
|
||||
self.vc_removeBackTitle()
|
||||
self.activityPresenter = ActivityIndicatorPresenter()
|
||||
self.errorPresenter = MXKErrorAlertPresentation()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
|
||||
self.viewModel.viewDelegate = self
|
||||
self.viewModel.process(viewAction: .loadData)
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
self.verifyButton.layer.cornerRadius = Constants.verifyButtonCornerRadius
|
||||
self.closeButton.layer.cornerRadius = self.closeButton.frame.size.width/2
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
|
||||
self.titleLabel.textColor = theme.textPrimaryColor
|
||||
self.closeButton.vc_setBackgroundColor(theme.headerTextSecondaryColor, for: .normal)
|
||||
|
||||
self.informationLabel.textColor = theme.textPrimaryColor
|
||||
|
||||
self.untrustedSessionInformationLabel.textColor = theme.textPrimaryColor
|
||||
self.verifyButton.vc_setBackgroundColor(theme.tintColor, for: .normal)
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
}
|
||||
|
||||
@objc private func themeDidChange() {
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
self.closeButton.layer.masksToBounds = true
|
||||
self.verifyButton.layer.masksToBounds = true
|
||||
}
|
||||
|
||||
private func render(viewState: UserVerificationSessionStatusViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded(viewData: let sessionStatusViewData):
|
||||
self.renderLoaded(viewData: sessionStatusViewData)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded(viewData: SessionStatusViewData) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
let badgeImage: UIImage
|
||||
let title: String
|
||||
|
||||
self.untrustedSessionContainerView.isHidden = viewData.isDeviceTrusted
|
||||
|
||||
if viewData.isDeviceTrusted {
|
||||
badgeImage = Asset.Images.encryptionTrusted.image
|
||||
title = VectorL10n.userVerificationSessionDetailsTrustedTitle
|
||||
} else {
|
||||
badgeImage = Asset.Images.encryptionWarning.image
|
||||
title = VectorL10n.userVerificationSessionDetailsUntrustedTitle
|
||||
}
|
||||
|
||||
let unstrustedInformationText: String
|
||||
let verifyButtonTitle: String
|
||||
|
||||
if viewData.isCurrentUser {
|
||||
unstrustedInformationText = VectorL10n.userVerificationSessionDetailsAdditionalInformationUntrustedCurrentUser
|
||||
verifyButtonTitle = VectorL10n.userVerificationSessionDetailsVerifyActionCurrentUser
|
||||
} else {
|
||||
unstrustedInformationText = VectorL10n.userVerificationSessionDetailsAdditionalInformationUntrustedOtherUser
|
||||
verifyButtonTitle = VectorL10n.userVerificationSessionDetailsVerifyActionOtherUser
|
||||
}
|
||||
|
||||
self.badgeImageView.image = badgeImage
|
||||
self.titleLabel.text = title
|
||||
self.informationLabel.attributedText = self.buildInformationAttributedText(with: viewData)
|
||||
|
||||
self.deviceStatusImageView.image = badgeImage
|
||||
self.deviceInformationLabel.attributedText = self.builDeviceInfoAttributedText(with: viewData)
|
||||
|
||||
self.untrustedSessionInformationLabel.text = unstrustedInformationText
|
||||
self.verifyButton.setTitle(verifyButtonTitle, for: .normal)
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
private func buildUserInfoText(with userId: String, userDisplayName: String?) -> String {
|
||||
|
||||
let userInfoText: String
|
||||
|
||||
if let userDisplayName = userDisplayName {
|
||||
userInfoText = "\(userDisplayName) (\(userId))"
|
||||
} else {
|
||||
userInfoText = userId
|
||||
}
|
||||
|
||||
return userInfoText
|
||||
}
|
||||
|
||||
private func buildInformationAttributedText(with viewData: SessionStatusViewData) -> NSAttributedString {
|
||||
|
||||
let informationAttributedText: NSMutableAttributedString = NSMutableAttributedString()
|
||||
|
||||
let informationTextDefaultAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: self.theme.textPrimaryColor,
|
||||
.font: Constants.informationTextDefaultFont]
|
||||
|
||||
let informationTextBoldAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: self.theme.textPrimaryColor,
|
||||
.font: Constants.informationTextBoldFont]
|
||||
|
||||
let userInfoText = self.buildUserInfoText(with: viewData.userId, userDisplayName: viewData.userDisplayName)
|
||||
|
||||
if viewData.isDeviceTrusted {
|
||||
|
||||
if viewData.isCurrentUser {
|
||||
let informationAttributedStringPart1 = NSAttributedString(string: VectorL10n.userVerificationSessionDetailsInformationTrustedCurrentUser, attributes: informationTextDefaultAttributes)
|
||||
informationAttributedText.append(informationAttributedStringPart1)
|
||||
} else {
|
||||
let informationAttributedStringPart1 = NSAttributedString(string: VectorL10n.userVerificationSessionDetailsInformationTrustedOtherUserPart1, attributes: informationTextDefaultAttributes)
|
||||
let informationAttributedStringPart2 = NSAttributedString(string: userInfoText, attributes: informationTextBoldAttributes)
|
||||
let informationAttributedStringPart3 = NSAttributedString(string: VectorL10n.userVerificationSessionDetailsInformationTrustedOtherUserPart2, attributes: informationTextDefaultAttributes)
|
||||
|
||||
informationAttributedText.append(informationAttributedStringPart1)
|
||||
informationAttributedText.append(informationAttributedStringPart2)
|
||||
informationAttributedText.append(informationAttributedStringPart3)
|
||||
}
|
||||
|
||||
} else {
|
||||
if viewData.isCurrentUser {
|
||||
let informationAttributedStringPart1 = NSAttributedString(string: VectorL10n.userVerificationSessionDetailsInformationUntrustedCurrentUser, attributes: informationTextDefaultAttributes)
|
||||
informationAttributedText.append(informationAttributedStringPart1)
|
||||
} else {
|
||||
let informationAttributedStringPart1 = NSAttributedString(string: userInfoText, attributes: informationTextBoldAttributes)
|
||||
let informationAttributedStringPart2 = NSAttributedString(string: VectorL10n.userVerificationSessionDetailsInformationUntrustedOtherUser, attributes: informationTextDefaultAttributes)
|
||||
|
||||
informationAttributedText.append(informationAttributedStringPart1)
|
||||
informationAttributedText.append(informationAttributedStringPart2)
|
||||
}
|
||||
}
|
||||
|
||||
return informationAttributedText
|
||||
}
|
||||
|
||||
private func builDeviceInfoAttributedText(with viewData: SessionStatusViewData) -> NSAttributedString {
|
||||
let deviceInfoAttributedText = NSMutableAttributedString()
|
||||
let deviceInfoAttributedTextPart1 = NSAttributedString(string: "\(viewData.deviceName) ", attributes: [.foregroundColor: self.theme.textPrimaryColor, .font: Constants.deviceNameFont])
|
||||
let deviceInfoAttributedTextPart2 = NSAttributedString(string: "(\(viewData.deviceId))", attributes: [.foregroundColor: self.theme.textSecondaryColor, .font: Constants.deviceIdFont])
|
||||
deviceInfoAttributedText.append(deviceInfoAttributedTextPart1)
|
||||
deviceInfoAttributedText.append(deviceInfoAttributedTextPart2)
|
||||
return deviceInfoAttributedText
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func closeButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .close)
|
||||
}
|
||||
|
||||
@IBAction private func verifyButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .verify)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UserVerificationSessionStatusViewModelViewDelegate
|
||||
extension UserVerificationSessionStatusViewController: UserVerificationSessionStatusViewModelViewDelegate {
|
||||
|
||||
func userVerificationSessionStatusViewModel(_ viewModel: UserVerificationSessionStatusViewModelType, didUpdateViewState viewSate: UserVerificationSessionStatusViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SessionStatusViewData {
|
||||
let userId: String
|
||||
let userDisplayName: String?
|
||||
let isCurrentUser: Bool
|
||||
|
||||
let deviceId: String
|
||||
let deviceName: String
|
||||
let isDeviceTrusted: Bool
|
||||
}
|
||||
|
||||
enum UserVerificationSessionStatusViewModelError: Error {
|
||||
case deviceNotFound
|
||||
}
|
||||
|
||||
final class UserVerificationSessionStatusViewModel: UserVerificationSessionStatusViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let userId: String
|
||||
private let userDisplayName: String?
|
||||
private let deviceId: String
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var viewDelegate: UserVerificationSessionStatusViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: UserVerificationSessionStatusViewModelCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, userId: String, userDisplayName: String?, deviceId: String) {
|
||||
self.session = session
|
||||
self.userId = userId
|
||||
self.userDisplayName = userDisplayName
|
||||
self.deviceId = deviceId
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: UserVerificationSessionStatusViewAction) {
|
||||
switch viewAction {
|
||||
case .loadData:
|
||||
self.loadData()
|
||||
case .verify:
|
||||
self.coordinatorDelegate?.userVerificationSessionStatusViewModel(self, wantsToManuallyVerifyDeviceWithId: self.deviceId, for: self.userId)
|
||||
case .close:
|
||||
self.coordinatorDelegate?.userVerificationSessionStatusViewModelDidClose(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func loadData() {
|
||||
guard let deviceInfo = self.session.crypto.device(withDeviceId: self.deviceId, ofUser: self.userId) else {
|
||||
self.update(viewState: .error(UserVerificationSessionStatusViewModelError.deviceNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
let isCurrentUser = self.session.myUser.userId == self.userId
|
||||
|
||||
let viewData = SessionStatusViewData(userId: self.userId,
|
||||
userDisplayName: self.userDisplayName,
|
||||
isCurrentUser: isCurrentUser,
|
||||
deviceId: deviceInfo.deviceId,
|
||||
deviceName: deviceInfo.displayName ?? "",
|
||||
isDeviceTrusted: deviceInfo.trustLevel.isVerified)
|
||||
self.update(viewState: .loaded(viewData: viewData))
|
||||
}
|
||||
|
||||
private func update(viewState: UserVerificationSessionStatusViewState) {
|
||||
self.viewDelegate?.userVerificationSessionStatusViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol UserVerificationSessionStatusViewModelViewDelegate: class {
|
||||
func userVerificationSessionStatusViewModel(_ viewModel: UserVerificationSessionStatusViewModelType, didUpdateViewState viewSate: UserVerificationSessionStatusViewState)
|
||||
}
|
||||
|
||||
protocol UserVerificationSessionStatusViewModelCoordinatorDelegate: class {
|
||||
func userVerificationSessionStatusViewModel(_ viewModel: UserVerificationSessionStatusViewModelType, wantsToManuallyVerifyDeviceWithId deviceId: String, for userId: String)
|
||||
func userVerificationSessionStatusViewModelDidClose(_ viewModel: UserVerificationSessionStatusViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `UserVerificationSessionStatusViewController`
|
||||
protocol UserVerificationSessionStatusViewModelType {
|
||||
|
||||
var viewDelegate: UserVerificationSessionStatusViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: UserVerificationSessionStatusViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: UserVerificationSessionStatusViewAction)
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SessionStatus UserVerificationSessionStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// UserVerificationSessionStatusViewController view state
|
||||
enum UserVerificationSessionStatusViewState {
|
||||
case loading
|
||||
case loaded(viewData: SessionStatusViewData)
|
||||
case error(Error)
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
struct UserVerificationSessionStatusViewData {
|
||||
let deviceId: String
|
||||
let sessionName: String
|
||||
let isTrusted: Bool
|
||||
}
|
||||
|
||||
final class UserVerificationSessionStatusCell: UITableViewCell, NibReusable, Themable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var statusImageView: UIImageView!
|
||||
@IBOutlet private weak var sessionNameLabel: UILabel!
|
||||
@IBOutlet private weak var statusTextLabel: UILabel!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewData: UserVerificationSessionStatusViewData?
|
||||
private var theme: Theme?
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func fill(viewData: UserVerificationSessionStatusViewData) {
|
||||
self.viewData = viewData
|
||||
|
||||
let statusText: String
|
||||
let statusImage: UIImage
|
||||
|
||||
if viewData.isTrusted {
|
||||
statusImage = Asset.Images.encryptionTrusted.image
|
||||
statusText = VectorL10n.userVerificationSessionsListSessionTrusted
|
||||
} else {
|
||||
statusImage = Asset.Images.encryptionWarning.image
|
||||
statusText = VectorL10n.userVerificationSessionsListSessionUntrusted
|
||||
}
|
||||
|
||||
self.statusImageView.image = statusImage
|
||||
self.statusTextLabel.text = statusText
|
||||
self.sessionNameLabel.text = viewData.sessionName
|
||||
|
||||
self.updateStatusTextColor()
|
||||
}
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
self.backgroundColor = theme.headerBackgroundColor
|
||||
self.sessionNameLabel.textColor = theme.textPrimaryColor
|
||||
self.updateStatusTextColor()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func updateStatusTextColor() {
|
||||
guard let viewData = self.viewData, let theme = self.theme else {
|
||||
return
|
||||
}
|
||||
self.statusTextLabel.textColor = viewData.isTrusted ? theme.tintColor : theme.warningColor
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="37" id="KGk-i7-Jjw" customClass="UserVerificationSessionStatusCell" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="307" height="37"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="307" height="36.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="V5p-pi-DJj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="307" height="36.5"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="encryption_warning" translatesAutoresizingMaskIntoConstraints="NO" id="4Ja-9R-o9i" customClass="encryption_warning">
|
||||
<rect key="frame" x="20" y="11.5" width="14" height="14"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="4Ja-9R-o9i" secondAttribute="height" multiplier="1:1" id="Ivd-S1-mib"/>
|
||||
<constraint firstAttribute="width" constant="14" id="n0o-Xb-nnn"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Riot iOS" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MkZ-9n-kCS">
|
||||
<rect key="frame" x="44" y="10" width="174.5" height="16.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="Trusted" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wsp-qe-DLy">
|
||||
<rect key="frame" x="228.5" y="8" width="58.5" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.01176470588" green="0.70196078429999997" blue="0.50588235290000005" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="MkZ-9n-kCS" firstAttribute="leading" secondItem="4Ja-9R-o9i" secondAttribute="trailing" constant="10" id="29O-OV-c6O"/>
|
||||
<constraint firstItem="4Ja-9R-o9i" firstAttribute="leading" secondItem="V5p-pi-DJj" secondAttribute="leading" constant="20" id="Gcu-EC-b72"/>
|
||||
<constraint firstItem="wsp-qe-DLy" firstAttribute="centerY" secondItem="MkZ-9n-kCS" secondAttribute="centerY" id="JQR-uR-OSm"/>
|
||||
<constraint firstAttribute="bottom" secondItem="MkZ-9n-kCS" secondAttribute="bottom" constant="10" id="Ohk-Zx-SaA"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wsp-qe-DLy" secondAttribute="trailing" constant="20" id="Onn-ew-tSg"/>
|
||||
<constraint firstItem="4Ja-9R-o9i" firstAttribute="centerY" secondItem="MkZ-9n-kCS" secondAttribute="centerY" id="U88-nG-bpa"/>
|
||||
<constraint firstItem="wsp-qe-DLy" firstAttribute="leading" secondItem="MkZ-9n-kCS" secondAttribute="trailing" constant="10" id="VdB-e0-Ru9"/>
|
||||
<constraint firstItem="MkZ-9n-kCS" firstAttribute="top" secondItem="V5p-pi-DJj" secondAttribute="top" constant="10" id="lx8-Ae-MX8"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="V5p-pi-DJj" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="3rQ-mA-tnY"/>
|
||||
<constraint firstItem="V5p-pi-DJj" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="PDK-oM-MhJ"/>
|
||||
<constraint firstAttribute="bottom" secondItem="V5p-pi-DJj" secondAttribute="bottom" id="dj0-s9-48N"/>
|
||||
<constraint firstAttribute="trailing" secondItem="V5p-pi-DJj" secondAttribute="trailing" id="lp6-TZ-ycQ"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
|
||||
<connections>
|
||||
<outlet property="sessionNameLabel" destination="MkZ-9n-kCS" id="atH-sc-sJx"/>
|
||||
<outlet property="statusImageView" destination="4Ja-9R-o9i" id="n6o-LV-b3O"/>
|
||||
<outlet property="statusTextLabel" destination="wsp-qe-DLy" id="4dV-vY-pmR"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="-72" y="26"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="encryption_warning" width="16" height="16"/>
|
||||
</resources>
|
||||
</document>
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
final class UserVerificationSessionsStatusCoordinator: UserVerificationSessionsStatusCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private var userVerificationSessionsStatusViewModel: UserVerificationSessionsStatusViewModelType
|
||||
private let userVerificationSessionsStatusViewController: UserVerificationSessionsStatusViewController
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: UserVerificationSessionsStatusCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, userId: String) {
|
||||
self.session = session
|
||||
|
||||
let userVerificationSessionsStatusViewModel = UserVerificationSessionsStatusViewModel(session: self.session, userId: userId)
|
||||
let userVerificationSessionsStatusViewController = UserVerificationSessionsStatusViewController.instantiate(with: userVerificationSessionsStatusViewModel)
|
||||
self.userVerificationSessionsStatusViewModel = userVerificationSessionsStatusViewModel
|
||||
self.userVerificationSessionsStatusViewController = userVerificationSessionsStatusViewController
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
self.userVerificationSessionsStatusViewModel.coordinatorDelegate = self
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.userVerificationSessionsStatusViewController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationSessionsStatusViewModelCoordinatorDelegate
|
||||
extension UserVerificationSessionsStatusCoordinator: UserVerificationSessionsStatusViewModelCoordinatorDelegate {
|
||||
func userVerificationSessionsStatusViewModel(_ viewModel: UserVerificationSessionsStatusViewModelType, didSelectDeviceWithId deviceId: String, for userId: String) {
|
||||
self.delegate?.userVerificationSessionsStatusCoordinator(self, didSelectDeviceWithId: deviceId, for: userId)
|
||||
}
|
||||
|
||||
func userVerificationSessionsStatusViewModelDidClose(_ viewModel: UserVerificationSessionsStatusViewModelType) {
|
||||
self.delegate?.userVerificationSessionsStatusCoordinatorDidClose(self)
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol UserVerificationSessionsStatusCoordinatorDelegate: class {
|
||||
func userVerificationSessionsStatusCoordinatorDidClose(_ coordinator: UserVerificationSessionsStatusCoordinatorType)
|
||||
func userVerificationSessionsStatusCoordinator(_ coordinator: UserVerificationSessionsStatusCoordinatorType, didSelectDeviceWithId deviceId: String, for userId: String)
|
||||
}
|
||||
|
||||
/// `UserVerificationSessionsStatusCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
|
||||
protocol UserVerificationSessionsStatusCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: UserVerificationSessionsStatusCoordinatorDelegate? { get }
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// UserVerificationSessionsStatusViewController view actions exposed to view model
|
||||
enum UserVerificationSessionsStatusViewAction {
|
||||
case loadData
|
||||
case selectSession(deviceId: String)
|
||||
case close
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="le3-e8-C1h">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--User Verification Sessions Status View Controller-->
|
||||
<scene sceneID="G1j-dD-ArC">
|
||||
<objects>
|
||||
<viewController id="le3-e8-C1h" customClass="UserVerificationSessionsStatusViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="IXd-J2-Vex">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Kfr-Ho-krK">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="108"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NBJ-A1-IP0">
|
||||
<rect key="frame" x="325" y="20" width="30" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="30" id="jda-sE-4Uz"/>
|
||||
<constraint firstAttribute="height" constant="30" id="rPb-uQ-Tyt"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<state key="normal" image="close_button"/>
|
||||
<connections>
|
||||
<action selector="closeButtonAction:" destination="le3-e8-C1h" eventType="touchUpInside" id="4JW-Rl-hIk"/>
|
||||
</connections>
|
||||
</button>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="encryption_warning" translatesAutoresizingMaskIntoConstraints="NO" id="rZH-h1-WTw">
|
||||
<rect key="frame" x="20" y="23" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="rZH-h1-WTw" secondAttribute="height" multiplier="1:1" id="RgU-aV-cmS"/>
|
||||
<constraint firstAttribute="height" constant="24" id="v0F-Dp-CmF"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Warning" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fcs-Vl-AtB">
|
||||
<rect key="frame" x="54" y="23" width="261" height="24"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Messages with this user in this room are end-to-end encrypted and can’t be read by third parties." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vfc-7p-LLs">
|
||||
<rect key="frame" x="20" y="62" width="335" height="36"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="NBJ-A1-IP0" firstAttribute="leading" secondItem="fcs-Vl-AtB" secondAttribute="trailing" constant="10" id="0cd-68-5tR"/>
|
||||
<constraint firstItem="fcs-Vl-AtB" firstAttribute="leading" secondItem="rZH-h1-WTw" secondAttribute="trailing" constant="10" id="75Z-TB-Hhj"/>
|
||||
<constraint firstItem="fcs-Vl-AtB" firstAttribute="centerY" secondItem="NBJ-A1-IP0" secondAttribute="centerY" id="DVH-2n-tXo"/>
|
||||
<constraint firstItem="Vfc-7p-LLs" firstAttribute="trailing" secondItem="NBJ-A1-IP0" secondAttribute="trailing" id="KQg-I1-5FP"/>
|
||||
<constraint firstItem="rZH-h1-WTw" firstAttribute="centerY" secondItem="fcs-Vl-AtB" secondAttribute="centerY" id="N8y-sA-Am7"/>
|
||||
<constraint firstItem="NBJ-A1-IP0" firstAttribute="top" secondItem="Kfr-Ho-krK" secondAttribute="top" constant="20" id="VUJ-Jj-UjR"/>
|
||||
<constraint firstAttribute="trailing" secondItem="NBJ-A1-IP0" secondAttribute="trailing" constant="20" id="ZH0-2n-XKy"/>
|
||||
<constraint firstItem="rZH-h1-WTw" firstAttribute="leading" secondItem="Kfr-Ho-krK" secondAttribute="leading" constant="20" id="gPw-ja-veq"/>
|
||||
<constraint firstItem="Vfc-7p-LLs" firstAttribute="top" secondItem="fcs-Vl-AtB" secondAttribute="bottom" constant="15" id="qh5-nR-1EN"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Vfc-7p-LLs" secondAttribute="bottom" constant="10" id="t5D-ht-cSc"/>
|
||||
<constraint firstItem="Vfc-7p-LLs" firstAttribute="leading" secondItem="rZH-h1-WTw" secondAttribute="leading" id="zAt-yf-7HQ"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sessions" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uMA-Dt-xTz">
|
||||
<rect key="frame" x="20" y="133" width="335" height="24"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="drr-Gd-5wA">
|
||||
<rect key="frame" x="0.0" y="167" width="375" height="500"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="le3-e8-C1h" id="Xmr-jN-S8s"/>
|
||||
<outlet property="delegate" destination="le3-e8-C1h" id="Y5J-nA-Ynp"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="drr-Gd-5wA" firstAttribute="leading" secondItem="IXd-J2-Vex" secondAttribute="leading" id="52r-4B-JdK"/>
|
||||
<constraint firstItem="Kfr-Ho-krK" firstAttribute="trailing" secondItem="Eud-3V-Vqw" secondAttribute="trailing" id="9Zg-EV-cHL"/>
|
||||
<constraint firstItem="Eud-3V-Vqw" firstAttribute="trailing" secondItem="uMA-Dt-xTz" secondAttribute="trailing" constant="20" id="Amb-5F-Ors"/>
|
||||
<constraint firstAttribute="trailing" secondItem="drr-Gd-5wA" secondAttribute="trailing" id="Awf-hM-uzJ"/>
|
||||
<constraint firstItem="drr-Gd-5wA" firstAttribute="top" secondItem="uMA-Dt-xTz" secondAttribute="bottom" constant="10" id="H1a-sI-AbP"/>
|
||||
<constraint firstAttribute="bottom" secondItem="drr-Gd-5wA" secondAttribute="bottom" id="Nhh-Ps-Upr"/>
|
||||
<constraint firstItem="Kfr-Ho-krK" firstAttribute="top" secondItem="Eud-3V-Vqw" secondAttribute="top" id="Wxs-Uw-xoA"/>
|
||||
<constraint firstItem="uMA-Dt-xTz" firstAttribute="leading" secondItem="Eud-3V-Vqw" secondAttribute="leading" constant="20" id="an1-BC-BP0"/>
|
||||
<constraint firstItem="uMA-Dt-xTz" firstAttribute="top" secondItem="Kfr-Ho-krK" secondAttribute="bottom" constant="5" id="wok-Oq-W0z"/>
|
||||
<constraint firstItem="Eud-3V-Vqw" firstAttribute="leading" secondItem="Kfr-Ho-krK" secondAttribute="leading" id="xjg-JA-ZUT"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="Eud-3V-Vqw"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="badgeImageImageView" destination="rZH-h1-WTw" id="M2a-7i-OoW"/>
|
||||
<outlet property="closeButton" destination="NBJ-A1-IP0" id="AYv-b7-fFp"/>
|
||||
<outlet property="informationLabel" destination="Vfc-7p-LLs" id="em9-jE-1wo"/>
|
||||
<outlet property="sessionsTableViewTitle" destination="uMA-Dt-xTz" id="vFU-UV-jMO"/>
|
||||
<outlet property="tableView" destination="drr-Gd-5wA" id="Cs0-pF-wwB"/>
|
||||
<outlet property="titleLabel" destination="fcs-Vl-AtB" id="iGp-kn-QfJ"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dBA-Zm-evE" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-463.19999999999999" y="-80.50974512743629"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="close_button" width="16" height="16"/>
|
||||
<image name="encryption_warning" width="16" height="16"/>
|
||||
</resources>
|
||||
</document>
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
final class UserVerificationSessionsStatusViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let estimatedRowHeight: CGFloat = 40.0
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var badgeImageImageView: UIImageView!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var closeButton: UIButton!
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
@IBOutlet private weak var sessionsTableViewTitle: UILabel!
|
||||
@IBOutlet private weak var tableView: UITableView!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: UserVerificationSessionsStatusViewModelType!
|
||||
private var theme: Theme!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityIndicatorPresenter: ActivityIndicatorPresenter!
|
||||
private var sessionsStatusViewData: [UserVerificationSessionStatusViewData] = []
|
||||
private var userEncryptionTrustLevel: UserEncryptionTrustLevel = .unknown
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: UserVerificationSessionsStatusViewModelType) -> UserVerificationSessionsStatusViewController {
|
||||
let viewController = StoryboardScene.UserVerificationSessionsStatusViewController.initialScene.instantiate()
|
||||
viewController.viewModel = viewModel
|
||||
viewController.theme = ThemeService.shared().theme
|
||||
return viewController
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.setupViews()
|
||||
self.vc_removeBackTitle()
|
||||
self.activityIndicatorPresenter = ActivityIndicatorPresenter()
|
||||
self.errorPresenter = MXKErrorAlertPresentation()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
|
||||
self.viewModel.viewDelegate = self
|
||||
|
||||
self.viewModel.process(viewAction: .loadData)
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
self.closeButton.layer.cornerRadius = self.closeButton.frame.size.width/2
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if let selectedIndexPath = self.tableView.indexPathForSelectedRow {
|
||||
self.tableView.deselectRow(at: selectedIndexPath, animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
|
||||
self.closeButton.vc_setBackgroundColor(theme.headerTextSecondaryColor, for: .normal)
|
||||
self.titleLabel.textColor = theme.textPrimaryColor
|
||||
self.informationLabel.textColor = theme.textPrimaryColor
|
||||
self.sessionsTableViewTitle.textColor = theme.textPrimaryColor
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
}
|
||||
|
||||
@objc private func themeDidChange() {
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
self.closeButton.layer.masksToBounds = true
|
||||
self.setupTableView()
|
||||
self.updateTitleViews()
|
||||
|
||||
self.sessionsTableViewTitle.text = VectorL10n.userVerificationSessionsListTableTitle
|
||||
self.informationLabel.text = VectorL10n.userVerificationSessionsListInformation
|
||||
}
|
||||
|
||||
private func setupTableView() {
|
||||
self.tableView.rowHeight = UITableView.automaticDimension
|
||||
self.tableView.estimatedRowHeight = Constants.estimatedRowHeight
|
||||
self.tableView.separatorStyle = .none
|
||||
self.tableView.tableFooterView = UIView()
|
||||
self.tableView.alwaysBounceVertical = false
|
||||
|
||||
self.tableView.register(cellType: UserVerificationSessionStatusCell.self)
|
||||
}
|
||||
|
||||
private func render(viewState: UserVerificationSessionsStatusViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded(userTrustLevel: let userTrustLevel, sessionsStatusViewData: let sessionsStatusViewData):
|
||||
self.renderLoaded(userTrustLevel: userTrustLevel, sessionsStatusViewData: sessionsStatusViewData)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.tableView.isUserInteractionEnabled = false
|
||||
self.activityIndicatorPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded(userTrustLevel: UserEncryptionTrustLevel, sessionsStatusViewData: [UserVerificationSessionStatusViewData]) {
|
||||
self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.tableView.isUserInteractionEnabled = true
|
||||
|
||||
self.userEncryptionTrustLevel = userTrustLevel
|
||||
self.sessionsStatusViewData = sessionsStatusViewData
|
||||
|
||||
self.updateTitleViews()
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.tableView.isUserInteractionEnabled = true
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
private func updateTitleViews() {
|
||||
|
||||
let badgeImage: UIImage
|
||||
let title: String
|
||||
|
||||
switch self.userEncryptionTrustLevel {
|
||||
case .trusted:
|
||||
badgeImage = Asset.Images.encryptionTrusted.image
|
||||
title = VectorL10n.userVerificationSessionsListUserTrustLevelTrustedTitle
|
||||
case .warning:
|
||||
badgeImage = Asset.Images.encryptionWarning.image
|
||||
title = VectorL10n.userVerificationSessionsListUserTrustLevelWarningTitle
|
||||
default:
|
||||
badgeImage = Asset.Images.encryptionNormal.image
|
||||
title = VectorL10n.userVerificationSessionsListUserTrustLevelUnknownTitle
|
||||
}
|
||||
|
||||
self.badgeImageImageView.image = badgeImage
|
||||
self.titleLabel.text = title
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func closeButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .close)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
extension UserVerificationSessionsStatusViewController: UITableViewDataSource {
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return self.sessionsStatusViewData.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(for: indexPath, cellType: UserVerificationSessionStatusCell.self)
|
||||
|
||||
let viewData = self.sessionsStatusViewData[indexPath.row]
|
||||
|
||||
cell.update(theme: self.theme)
|
||||
cell.fill(viewData: viewData)
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
extension UserVerificationSessionsStatusViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let viewData = self.sessionsStatusViewData[indexPath.row]
|
||||
self.viewModel.process(viewAction: .selectSession(deviceId: viewData.deviceId))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationSessionsStatusViewModelViewDelegate
|
||||
extension UserVerificationSessionsStatusViewController: UserVerificationSessionsStatusViewModelViewDelegate {
|
||||
|
||||
func userVerificationSessionsStatusViewModel(_ viewModel: UserVerificationSessionsStatusViewModelType, didUpdateViewState viewSate: UserVerificationSessionsStatusViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
enum UserVerificationSessionsStatusViewModelError: Error {
|
||||
case unknown
|
||||
}
|
||||
|
||||
final class UserVerificationSessionsStatusViewModel: UserVerificationSessionsStatusViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let userId: String
|
||||
private var currentOperation: MXHTTPOperation?
|
||||
private var userTrustLevel: UserEncryptionTrustLevel
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var viewDelegate: UserVerificationSessionsStatusViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: UserVerificationSessionsStatusViewModelCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, userId: String) {
|
||||
self.session = session
|
||||
self.userId = userId
|
||||
self.userTrustLevel = .unknown
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.currentOperation?.cancel()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: UserVerificationSessionsStatusViewAction) {
|
||||
switch viewAction {
|
||||
case .loadData:
|
||||
self.loadData()
|
||||
case .selectSession(deviceId: let deviceId):
|
||||
self.coordinatorDelegate?.userVerificationSessionsStatusViewModel(self, didSelectDeviceWithId: deviceId, for: self.userId)
|
||||
case .close:
|
||||
self.coordinatorDelegate?.userVerificationSessionsStatusViewModelDidClose(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func loadData() {
|
||||
|
||||
let sessionsStatusViewData = self.getSessionStatusViewDataListFromCache(for: self.userId)
|
||||
self.update(viewState: .loaded(userTrustLevel: self.userTrustLevel, sessionsStatusViewData: sessionsStatusViewData))
|
||||
|
||||
self.fetchSessionStatus()
|
||||
}
|
||||
|
||||
private func update(viewState: UserVerificationSessionsStatusViewState) {
|
||||
self.viewDelegate?.userVerificationSessionsStatusViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
|
||||
private func fetchSessionStatus() {
|
||||
self.update(viewState: .loading)
|
||||
|
||||
self.currentOperation = self.getSessionStatusViewDataList(for: self.userId) { result in
|
||||
switch result {
|
||||
case .success(let sessionsStatusViewData):
|
||||
|
||||
let isUserTrusted = sessionsStatusViewData.contains(where: { sessionsStatusViewData -> Bool in
|
||||
return sessionsStatusViewData.isTrusted == false
|
||||
}) == false
|
||||
|
||||
let userTrustLevel: UserEncryptionTrustLevel = isUserTrusted ? .trusted : .warning
|
||||
|
||||
self.update(viewState: .loaded(userTrustLevel: userTrustLevel, sessionsStatusViewData: sessionsStatusViewData))
|
||||
case .failure(let error):
|
||||
self.update(viewState: .error(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func getSessionStatusViewDataListFromCache(for userId: String) -> [UserVerificationSessionStatusViewData] {
|
||||
let deviceInfoList = self.getDevicesFromCache(for: self.userId)
|
||||
return self.sessionStatusViewDataList(from: deviceInfoList)
|
||||
}
|
||||
|
||||
private func getDevicesFromCache(for userId: String) -> [MXDeviceInfo] {
|
||||
guard let deviceInfoMap = self.session.crypto.devices(forUser: self.userId) else {
|
||||
return []
|
||||
}
|
||||
return Array(deviceInfoMap.values)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
private func getSessionStatusViewDataList(for userId: String, completion: @escaping (Result<[UserVerificationSessionStatusViewData], Error>) -> Void) -> MXHTTPOperation? {
|
||||
|
||||
let httpOperation: MXHTTPOperation?
|
||||
|
||||
httpOperation = self.session.crypto.downloadKeys([self.userId], forceDownload: false, success: { ( usersDeviceMap: MXUsersDevicesMap<MXDeviceInfo>?, usersCrossSigningMap: [String : MXCrossSigningInfo]?) in
|
||||
|
||||
let sessionsViewData: [UserVerificationSessionStatusViewData]
|
||||
|
||||
if let usersDeviceMap = usersDeviceMap, let userDeviceInfoMap = Array(usersDeviceMap.map.values).first {
|
||||
let deviceInfoList = Array(userDeviceInfoMap.values)
|
||||
sessionsViewData = self.sessionStatusViewDataList(from: deviceInfoList)
|
||||
} else {
|
||||
sessionsViewData = []
|
||||
}
|
||||
|
||||
completion(.success(sessionsViewData))
|
||||
|
||||
}, failure: { error in
|
||||
|
||||
let finalError = error ?? UserVerificationSessionsStatusViewModelError.unknown
|
||||
completion(.failure(finalError))
|
||||
})
|
||||
|
||||
return httpOperation
|
||||
}
|
||||
|
||||
private func sessionStatusViewData(from deviceInfo: MXDeviceInfo) -> UserVerificationSessionStatusViewData {
|
||||
return UserVerificationSessionStatusViewData(deviceId: deviceInfo.deviceId, sessionName: deviceInfo.displayName ?? "", isTrusted: deviceInfo.trustLevel.isVerified)
|
||||
}
|
||||
|
||||
private func sessionStatusViewDataList(from deviceInfoList: [MXDeviceInfo]) -> [UserVerificationSessionStatusViewData] {
|
||||
return deviceInfoList.map { (deviceInfo) -> UserVerificationSessionStatusViewData in
|
||||
return self.sessionStatusViewData(from: deviceInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol UserVerificationSessionsStatusViewModelViewDelegate: class {
|
||||
func userVerificationSessionsStatusViewModel(_ viewModel: UserVerificationSessionsStatusViewModelType, didUpdateViewState viewSate: UserVerificationSessionsStatusViewState)
|
||||
}
|
||||
|
||||
protocol UserVerificationSessionsStatusViewModelCoordinatorDelegate: class {
|
||||
func userVerificationSessionsStatusViewModel(_ viewModel: UserVerificationSessionsStatusViewModelType, didSelectDeviceWithId deviceId: String, for userId: String)
|
||||
func userVerificationSessionsStatusViewModelDidClose(_ viewModel: UserVerificationSessionsStatusViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `UserVerificationSessionsStatusViewController`
|
||||
protocol UserVerificationSessionsStatusViewModelType {
|
||||
|
||||
var viewDelegate: UserVerificationSessionsStatusViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: UserVerificationSessionsStatusViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: UserVerificationSessionsStatusViewAction)
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh UserVerification UserVerificationSessionsStatus
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// UserVerificationSessionsStatusViewController view state
|
||||
enum UserVerificationSessionsStatusViewState {
|
||||
case loading
|
||||
case loaded(userTrustLevel: UserEncryptionTrustLevel, sessionsStatusViewData: [UserVerificationSessionStatusViewData])
|
||||
case error(Error)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
final class UserVerificationStartCoordinator: UserVerificationStartCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let roomMember: MXRoomMember
|
||||
|
||||
private var userVerificationStartViewModel: UserVerificationStartViewModelType
|
||||
private let userVerificationStartViewController: UserVerificationStartViewController
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: UserVerificationStartCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, roomMember: MXRoomMember) {
|
||||
self.session = session
|
||||
self.roomMember = roomMember
|
||||
|
||||
let userVerificationStartViewModel = UserVerificationStartViewModel(session: self.session, roomMember: self.roomMember)
|
||||
let userVerificationStartViewController = UserVerificationStartViewController.instantiate(with: userVerificationStartViewModel)
|
||||
self.userVerificationStartViewModel = userVerificationStartViewModel
|
||||
self.userVerificationStartViewController = userVerificationStartViewController
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
self.userVerificationStartViewModel.coordinatorDelegate = self
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.userVerificationStartViewController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationStartViewModelCoordinatorDelegate
|
||||
extension UserVerificationStartCoordinator: UserVerificationStartViewModelCoordinatorDelegate {
|
||||
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, otherDidAcceptRequest request: MXKeyVerificationRequest) {
|
||||
self.delegate?.userVerificationStartCoordinator(self, otherDidAcceptRequest: request)
|
||||
}
|
||||
|
||||
func userVerificationStartViewModelDidCancel(_ viewModel: UserVerificationStartViewModelType) {
|
||||
self.delegate?.userVerificationStartCoordinatorDidCancel(self)
|
||||
}
|
||||
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didCompleteWithIncomingTransaction transaction: MXSASTransaction) {
|
||||
self.delegate?.userVerificationStartCoordinator(self, didCompleteWithOutgoingTransaction: transaction)
|
||||
}
|
||||
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction) {
|
||||
self.delegate?.userVerificationStartCoordinator(self, didTransactionCancelled: transaction)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol UserVerificationStartCoordinatorDelegate: class {
|
||||
|
||||
func userVerificationStartCoordinator(_ coordinator: UserVerificationStartCoordinatorType, otherDidAcceptRequest request: MXKeyVerificationRequest)
|
||||
|
||||
func userVerificationStartCoordinator(_ coordinator: UserVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction)
|
||||
|
||||
func userVerificationStartCoordinator(_ coordinator: UserVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction)
|
||||
|
||||
func userVerificationStartCoordinatorDidCancel(_ coordinator: UserVerificationStartCoordinatorType)
|
||||
}
|
||||
|
||||
/// `UserVerificationStartCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
|
||||
protocol UserVerificationStartCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: UserVerificationStartCoordinatorDelegate? { get }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// UserVerificationStartViewController view actions exposed to view model
|
||||
enum UserVerificationStartViewAction {
|
||||
case loadData
|
||||
case startVerification
|
||||
case cancel
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--User Verification Start View Controller-->
|
||||
<scene sceneID="mt5-wz-YKA">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="UserVerificationStartViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="EL9-GA-lwo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dQD-Vs-7h6">
|
||||
<rect key="frame" x="0.0" y="254" width="375" height="179"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="For extra security, verify @user:matrix.org by checking a one-time code on both your devices." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="b2K-LP-jqv">
|
||||
<rect key="frame" x="20" y="10" width="335" height="36"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Waiting for User…" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0QC-yi-0hN">
|
||||
<rect key="frame" x="20" y="78" width="335" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3sZ-h5-05D">
|
||||
<rect key="frame" x="20" y="66" width="335" height="44"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCLoginButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="2qO-rj-st6"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="8kK-yb-srh"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<inset key="contentEdgeInsets" minX="30" minY="0.0" maxX="30" maxY="0.0"/>
|
||||
<state key="normal" title="Start Verification">
|
||||
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="startVerificationButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="3Nd-Km-ucJ"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="To be secure, do this in person or use another way to communicate." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vli-IZ-UjP">
|
||||
<rect key="frame" x="20" y="130" width="335" height="29"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="3sZ-h5-05D" firstAttribute="centerX" secondItem="dQD-Vs-7h6" secondAttribute="centerX" id="2OM-Zh-gnO"/>
|
||||
<constraint firstAttribute="trailing" secondItem="0QC-yi-0hN" secondAttribute="trailing" constant="20" id="70Z-qZ-AG4"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="3sZ-h5-05D" secondAttribute="trailing" constant="20" id="ABg-Iy-IdN"/>
|
||||
<constraint firstItem="Vli-IZ-UjP" firstAttribute="top" secondItem="3sZ-h5-05D" secondAttribute="bottom" constant="20" id="BIE-UM-I0Z"/>
|
||||
<constraint firstItem="3sZ-h5-05D" firstAttribute="top" secondItem="b2K-LP-jqv" secondAttribute="bottom" constant="20" id="HCf-2F-jBr"/>
|
||||
<constraint firstItem="Vli-IZ-UjP" firstAttribute="leading" secondItem="dQD-Vs-7h6" secondAttribute="leading" constant="20" id="Nz0-mv-fF5"/>
|
||||
<constraint firstItem="3sZ-h5-05D" firstAttribute="width" secondItem="dQD-Vs-7h6" secondAttribute="width" priority="250" id="RuV-mq-5JZ"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Vli-IZ-UjP" secondAttribute="trailing" constant="20" id="XXe-5j-SxE"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Vli-IZ-UjP" secondAttribute="bottom" constant="20" id="Yfh-kG-UKJ"/>
|
||||
<constraint firstItem="b2K-LP-jqv" firstAttribute="leading" secondItem="dQD-Vs-7h6" secondAttribute="leading" constant="20" id="dgE-c5-D4E"/>
|
||||
<constraint firstItem="0QC-yi-0hN" firstAttribute="leading" secondItem="dQD-Vs-7h6" secondAttribute="leading" constant="20" id="g4e-WC-LQ7"/>
|
||||
<constraint firstItem="0QC-yi-0hN" firstAttribute="centerY" secondItem="3sZ-h5-05D" secondAttribute="centerY" id="hqE-9r-yhx"/>
|
||||
<constraint firstAttribute="height" priority="250" id="j1W-Y7-dYc"/>
|
||||
<constraint firstItem="3sZ-h5-05D" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="dQD-Vs-7h6" secondAttribute="leading" constant="20" id="oP3-BI-kYI"/>
|
||||
<constraint firstAttribute="trailing" secondItem="b2K-LP-jqv" secondAttribute="trailing" constant="20" id="uJ5-fk-7Xw"/>
|
||||
<constraint firstItem="b2K-LP-jqv" firstAttribute="top" secondItem="dQD-Vs-7h6" secondAttribute="top" constant="10" id="vlb-Yg-HWD"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.94509803921568625" green="0.96078431372549022" blue="0.97254901960784312" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="dQD-Vs-7h6" firstAttribute="centerY" secondItem="bFg-jh-JZB" secondAttribute="centerY" id="GmI-7r-ejI"/>
|
||||
<constraint firstItem="dQD-Vs-7h6" firstAttribute="trailing" secondItem="bFg-jh-JZB" secondAttribute="trailing" id="XX2-fS-8cb"/>
|
||||
<constraint firstItem="dQD-Vs-7h6" firstAttribute="leading" secondItem="bFg-jh-JZB" secondAttribute="leading" id="yKt-js-MnW"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="additionalInformationLabel" destination="Vli-IZ-UjP" id="4UB-LL-Om3"/>
|
||||
<outlet property="informationLabel" destination="b2K-LP-jqv" id="O4J-7t-Dn6"/>
|
||||
<outlet property="startVerificationButton" destination="3sZ-h5-05D" id="vNi-m9-x7g"/>
|
||||
<outlet property="verificationWaitingLabel" destination="0QC-yi-0hN" id="Qoz-cV-Su7"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-3198" y="-647"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,230 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
final class UserVerificationStartViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let verifyButtonCornerRadius: CGFloat = 8.0
|
||||
static let informationTextDefaultFont = UIFont.systemFont(ofSize: 15.0)
|
||||
static let informationTextBoldFont = UIFont.systemFont(ofSize: 15.0, weight: .medium)
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var startVerificationButton: UIButton!
|
||||
@IBOutlet private weak var verificationWaitingLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var additionalInformationLabel: UILabel!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: UserVerificationStartViewModelType!
|
||||
private var theme: Theme!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: UserVerificationStartViewModelType) -> UserVerificationStartViewController {
|
||||
let viewController = StoryboardScene.UserVerificationStartViewController.initialScene.instantiate()
|
||||
viewController.viewModel = viewModel
|
||||
viewController.theme = ThemeService.shared().theme
|
||||
return viewController
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.title = VectorL10n.keyVerificationUserTitle
|
||||
|
||||
self.setupViews()
|
||||
|
||||
self.activityPresenter = ActivityIndicatorPresenter()
|
||||
self.errorPresenter = MXKErrorAlertPresentation()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
|
||||
self.viewModel.viewDelegate = self
|
||||
|
||||
self.viewModel.process(viewAction: .loadData)
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
self.startVerificationButton.layer.cornerRadius = Constants.verifyButtonCornerRadius
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
|
||||
self.informationLabel.textColor = theme.textPrimaryColor
|
||||
self.startVerificationButton.vc_setBackgroundColor(theme.tintColor, for: .normal)
|
||||
self.verificationWaitingLabel.textColor = theme.textSecondaryColor
|
||||
self.additionalInformationLabel.textColor = theme.textSecondaryColor
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
}
|
||||
|
||||
@objc private func themeDidChange() {
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in
|
||||
self?.cancelButtonAction()
|
||||
}
|
||||
|
||||
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
|
||||
|
||||
self.startVerificationButton.layer.masksToBounds = true
|
||||
self.startVerificationButton.setTitle(VectorL10n.userVerificationStartVerifyAction, for: .normal)
|
||||
self.additionalInformationLabel.text = VectorL10n.userVerificationStartAdditionalInformation
|
||||
}
|
||||
|
||||
private func render(viewState: UserVerificationStartViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded(let viewData):
|
||||
self.renderLoaded(viewData: viewData)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
case .verificationPending:
|
||||
self.renderVerificationPending()
|
||||
case .cancelled(let reason):
|
||||
self.renderCancelled(reason: reason)
|
||||
case .cancelledByMe(let reason):
|
||||
self.renderCancelledByMe(reason: reason)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded(viewData: UserVerificationStartViewData) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.informationLabel.attributedText = self.buildInformationAttributedText(with: viewData.userId)
|
||||
self.verificationWaitingLabel.text = self.buildVerificationWaitingText(with: viewData)
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
private func renderVerificationPending() {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.startVerificationButton.isHidden = true
|
||||
self.verificationWaitingLabel.isHidden = false
|
||||
}
|
||||
|
||||
private func renderCancelled(reason: MXTransactionCancelCode) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.errorPresenter.presentError(from: self, title: "", message: VectorL10n.deviceVerificationCancelled, animated: true) {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderCancelledByMe(reason: MXTransactionCancelCode) {
|
||||
if reason.value != MXTransactionCancelCode.user().value {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.errorPresenter.presentError(from: self, title: "", message: VectorL10n.deviceVerificationCancelledByMe(reason.humanReadable), animated: true) {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
} else {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func buildInformationAttributedText(with userId: String) -> NSAttributedString {
|
||||
|
||||
let informationAttributedText: NSMutableAttributedString = NSMutableAttributedString()
|
||||
|
||||
let informationTextDefaultAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: self.theme.textPrimaryColor,
|
||||
.font: Constants.informationTextDefaultFont]
|
||||
|
||||
let informationTextBoldAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: self.theme.textPrimaryColor,
|
||||
.font: Constants.informationTextBoldFont]
|
||||
|
||||
let informationAttributedStringPart1 = NSAttributedString(string: VectorL10n.userVerificationStartInformationPart1, attributes: informationTextDefaultAttributes)
|
||||
let informationAttributedStringPart2 = NSAttributedString(string: userId, attributes: informationTextBoldAttributes)
|
||||
let informationAttributedStringPart3 = NSAttributedString(string: VectorL10n.userVerificationStartInformationPart2, attributes: informationTextDefaultAttributes)
|
||||
|
||||
informationAttributedText.append(informationAttributedStringPart1)
|
||||
informationAttributedText.append(informationAttributedStringPart2)
|
||||
informationAttributedText.append(informationAttributedStringPart3)
|
||||
|
||||
return informationAttributedText
|
||||
}
|
||||
|
||||
private func buildVerificationWaitingText(with viewData: UserVerificationStartViewData) -> String {
|
||||
let userName = viewData.userDisplayName ?? viewData.userId
|
||||
return VectorL10n.userVerificationStartWaitingPartner(userName)
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func startVerificationButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .startVerification)
|
||||
}
|
||||
|
||||
private func cancelButtonAction() {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UserVerificationStartViewModelViewDelegate
|
||||
extension UserVerificationStartViewController: UserVerificationStartViewModelViewDelegate {
|
||||
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didUpdateViewState viewSate: UserVerificationStartViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
enum UserVerificationStartViewModelError: Error {
|
||||
case keyVerificationRequestExpired
|
||||
}
|
||||
|
||||
struct UserVerificationStartViewData {
|
||||
let userId: String
|
||||
let userDisplayName: String?
|
||||
let userAvatarURL: String?
|
||||
}
|
||||
|
||||
final class UserVerificationStartViewModel: UserVerificationStartViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let roomMember: MXRoomMember
|
||||
private let verificationManager: MXKeyVerificationManager
|
||||
private let keyVerificationService: KeyVerificationService
|
||||
|
||||
private var keyVerificationRequest: MXKeyVerificationRequest?
|
||||
|
||||
private var viewData: UserVerificationStartViewData {
|
||||
return UserVerificationStartViewData(userId: self.roomMember.userId, userDisplayName: self.roomMember.displayname, userAvatarURL: self.roomMember.avatarUrl)
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var viewDelegate: UserVerificationStartViewModelViewDelegate?
|
||||
weak var coordinatorDelegate: UserVerificationStartViewModelCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, roomMember: MXRoomMember) {
|
||||
self.session = session
|
||||
self.verificationManager = session.crypto.keyVerificationManager
|
||||
self.roomMember = roomMember
|
||||
self.keyVerificationService = KeyVerificationService()
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: UserVerificationStartViewAction) {
|
||||
switch viewAction {
|
||||
case .loadData:
|
||||
self.loadData()
|
||||
case .startVerification:
|
||||
self.startVerification()
|
||||
case .cancel:
|
||||
self.cancelKeyVerificationRequest()
|
||||
self.coordinatorDelegate?.userVerificationStartViewModelDidCancel(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func loadData() {
|
||||
self.update(viewState: .loaded(self.viewData))
|
||||
}
|
||||
|
||||
private func startVerification() {
|
||||
self.update(viewState: .verificationPending)
|
||||
|
||||
self.verificationManager.requestVerificationByDM(withUserId: self.roomMember.userId,
|
||||
roomId: nil,
|
||||
fallbackText: "",
|
||||
methods: self.keyVerificationService.supportedKeyVerificationMethods(),
|
||||
success: { [weak self] (keyVerificationRequest) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.keyVerificationRequest = keyVerificationRequest
|
||||
self.update(viewState: .loaded(self.viewData))
|
||||
self.registerKeyVerificationRequestDidChangeNotification(for: keyVerificationRequest)
|
||||
}, failure: { [weak self] error in
|
||||
self?.update(viewState: .error(error))
|
||||
})
|
||||
}
|
||||
|
||||
private func update(viewState: UserVerificationStartViewState) {
|
||||
self.viewDelegate?.userVerificationStartViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
|
||||
private func cancelKeyVerificationRequest() {
|
||||
guard let keyVerificationRequest = self.keyVerificationRequest else {
|
||||
return
|
||||
}
|
||||
|
||||
keyVerificationRequest.cancel(with: MXTransactionCancelCode.user(), success: nil, failure: nil)
|
||||
}
|
||||
|
||||
// MARK: - MXKeyVerificationRequestDidChange
|
||||
|
||||
private func registerKeyVerificationRequestDidChangeNotification(for keyVerificationRequest: MXKeyVerificationRequest) {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(keyVerificationRequestDidChange(notification:)), name: .MXKeyVerificationRequestDidChange, object: keyVerificationRequest)
|
||||
}
|
||||
|
||||
private func unregisterKeyVerificationRequestDidChangeNotification() {
|
||||
NotificationCenter.default.removeObserver(self, name: .MXKeyVerificationRequestDidChange, object: nil)
|
||||
}
|
||||
|
||||
@objc private func keyVerificationRequestDidChange(notification: Notification) {
|
||||
guard let keyVerificationRequest = notification.object as? MXKeyVerificationByDMRequest else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let currentKeyVerificationRequest = self.keyVerificationRequest, keyVerificationRequest.requestId == currentKeyVerificationRequest.requestId else {
|
||||
return
|
||||
}
|
||||
|
||||
switch keyVerificationRequest.state {
|
||||
case MXKeyVerificationRequestStateAccepted:
|
||||
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||
self.coordinatorDelegate?.userVerificationStartViewModel(self, otherDidAcceptRequest: currentKeyVerificationRequest)
|
||||
case MXKeyVerificationRequestStateReady:
|
||||
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||
self.coordinatorDelegate?.userVerificationStartViewModel(self, otherDidAcceptRequest: currentKeyVerificationRequest)
|
||||
case MXKeyVerificationRequestStateCancelled:
|
||||
guard let reason = keyVerificationRequest.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||
self.update(viewState: .cancelled(reason))
|
||||
case MXKeyVerificationRequestStateCancelledByMe:
|
||||
guard let reason = keyVerificationRequest.reasonCancelCode else {
|
||||
return
|
||||
}
|
||||
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||
self.update(viewState: .cancelledByMe(reason))
|
||||
case MXKeyVerificationRequestStateExpired:
|
||||
self.unregisterKeyVerificationRequestDidChangeNotification()
|
||||
self.update(viewState: .error(UserVerificationStartViewModelError.keyVerificationRequestExpired))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol UserVerificationStartViewModelViewDelegate: class {
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didUpdateViewState viewSate: UserVerificationStartViewState)
|
||||
}
|
||||
|
||||
protocol UserVerificationStartViewModelCoordinatorDelegate: class {
|
||||
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, otherDidAcceptRequest request: MXKeyVerificationRequest)
|
||||
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didCompleteWithIncomingTransaction transaction: MXSASTransaction)
|
||||
|
||||
func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction)
|
||||
|
||||
func userVerificationStartViewModelDidCancel(_ viewModel: UserVerificationStartViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `UserVerificationStartViewController`
|
||||
protocol UserVerificationStartViewModelType {
|
||||
|
||||
var viewDelegate: UserVerificationStartViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: UserVerificationStartViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: UserVerificationStartViewAction)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh Start UserVerificationStart
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// UserVerificationStartViewController view state
|
||||
enum UserVerificationStartViewState {
|
||||
case loading
|
||||
case loaded(UserVerificationStartViewData)
|
||||
case verificationPending
|
||||
case cancelled(MXTransactionCancelCode)
|
||||
case cancelledByMe(MXTransactionCancelCode)
|
||||
case error(Error)
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh UserVerification UserVerification
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers
|
||||
final class UserVerificationCoordinator: NSObject, UserVerificationCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let presenter: Presentable
|
||||
private let navigationRouter: NavigationRouterType
|
||||
private let session: MXSession
|
||||
private let userId: String
|
||||
private let userDisplayName: String?
|
||||
private var deviceId: String?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: UserVerificationCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(presenter: Presentable, session: MXSession, userId: String, userDisplayName: String?) {
|
||||
self.presenter = presenter
|
||||
self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController())
|
||||
self.session = session
|
||||
self.userId = userId
|
||||
self.userDisplayName = userDisplayName
|
||||
}
|
||||
|
||||
convenience init(presenter: Presentable, session: MXSession, userId: String, userDisplayName: String?, deviceId: String) {
|
||||
self.init(presenter: presenter, session: session, userId: userId, userDisplayName: userDisplayName)
|
||||
self.deviceId = deviceId
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
// Do not start again if existing coordinators are presented
|
||||
guard self.childCoordinators.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
guard self.session.crypto.crossSigning.isBootstrapped else {
|
||||
self.presentBootstrapNotSetup()
|
||||
return
|
||||
}
|
||||
|
||||
let rootCoordinator: Coordinator & Presentable
|
||||
|
||||
if let deviceId = self.deviceId {
|
||||
rootCoordinator = self.createSessionStatusCoordinator(with: deviceId, for: self.userId, userDisplayName: self.userDisplayName)
|
||||
} else {
|
||||
rootCoordinator = self.createUserVerificationSessionsStatusCoordinator()
|
||||
}
|
||||
|
||||
rootCoordinator.start()
|
||||
|
||||
self.add(childCoordinator: rootCoordinator)
|
||||
|
||||
self.navigationRouter.setRootModule(rootCoordinator, hideNavigationBar: true, animated: false, popCompletion: {
|
||||
self.remove(childCoordinator: rootCoordinator)
|
||||
})
|
||||
|
||||
let rootViewController = self.navigationRouter.toPresentable()
|
||||
rootViewController.modalPresentationStyle = .formSheet
|
||||
|
||||
self.presenter.toPresentable().present(rootViewController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.navigationRouter.toPresentable()
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private func createUserVerificationSessionsStatusCoordinator() -> UserVerificationSessionsStatusCoordinator {
|
||||
let coordinator = UserVerificationSessionsStatusCoordinator(session: self.session, userId: self.userId)
|
||||
coordinator.delegate = self
|
||||
return coordinator
|
||||
}
|
||||
|
||||
private func createSessionStatusCoordinator(with deviceId: String, for userId: String, userDisplayName: String?) -> UserVerificationSessionStatusCoordinator {
|
||||
let coordinator = UserVerificationSessionStatusCoordinator(session: self.session, userId: userId, userDisplayName: userDisplayName, deviceId: deviceId)
|
||||
coordinator.delegate = self
|
||||
return coordinator
|
||||
}
|
||||
|
||||
private func presentSessionStatus(with deviceId: String, for userId: String, userDisplayName: String?) {
|
||||
let coordinator = self.createSessionStatusCoordinator(with: deviceId, for: userId, userDisplayName: userDisplayName)
|
||||
coordinator.start()
|
||||
|
||||
self.navigationRouter.push(coordinator, animated: true) {
|
||||
self.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
private func presentDeviceVerification(for deviceId: String) {
|
||||
|
||||
let keyVerificationCoordinator = KeyVerificationCoordinator(navigationRouter: self.navigationRouter, session: self.session, userId: self.userId, otherDeviceId: deviceId)
|
||||
keyVerificationCoordinator.delegate = self
|
||||
keyVerificationCoordinator.start()
|
||||
|
||||
self.add(childCoordinator: keyVerificationCoordinator)
|
||||
|
||||
self.navigationRouter.push(keyVerificationCoordinator, animated: true, popCompletion: {
|
||||
self.remove(childCoordinator: keyVerificationCoordinator)
|
||||
})
|
||||
}
|
||||
|
||||
private func presentBootstrapNotSetup() {
|
||||
let alert = UIAlertController(title: VectorL10n.keyVerificationBootstrapNotSetupTitle,
|
||||
message: VectorL10n.keyVerificationBootstrapNotSetupMessage,
|
||||
preferredStyle: .alert)
|
||||
|
||||
let cancelAction = UIAlertAction(title: Bundle.mxk_localizedString(forKey: "ok"), style: .cancel, handler: { _ in
|
||||
})
|
||||
alert.addAction(cancelAction)
|
||||
|
||||
self.presenter.toPresentable().present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationSessionsStatusCoordinatorDelegate
|
||||
extension UserVerificationCoordinator: UserVerificationSessionsStatusCoordinatorDelegate {
|
||||
func userVerificationSessionsStatusCoordinatorDidClose(_ coordinator: UserVerificationSessionsStatusCoordinatorType) {
|
||||
|
||||
self.presenter.toPresentable().dismiss(animated: true) {
|
||||
self.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
func userVerificationSessionsStatusCoordinator(_ coordinator: UserVerificationSessionsStatusCoordinatorType, didSelectDeviceWithId deviceId: String, for userId: String) {
|
||||
self.presentSessionStatus(with: deviceId, for: userId, userDisplayName: self.userDisplayName)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationSessionStatusCoordinatorDelegate
|
||||
extension UserVerificationCoordinator: UserVerificationSessionStatusCoordinatorDelegate {
|
||||
|
||||
func userVerificationSessionStatusCoordinator(_ coordinator: UserVerificationSessionStatusCoordinatorType, wantsToManuallyVerifyDeviceWithId deviceId: String, for userId: String) {
|
||||
|
||||
self.presentDeviceVerification(for: deviceId)
|
||||
}
|
||||
|
||||
func userVerificationSessionStatusCoordinatorDidClose(_ coordinator: UserVerificationSessionStatusCoordinatorType) {
|
||||
|
||||
self.presenter.toPresentable().dismiss(animated: true) {
|
||||
self.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserVerificationCoordinatorDelegate
|
||||
extension UserVerificationCoordinator: KeyVerificationCoordinatorDelegate {
|
||||
|
||||
func keyVerificationCoordinatorDidComplete(_ coordinator: KeyVerificationCoordinatorType, otherUserId: String, otherDeviceId: String) {
|
||||
self.presenter.toPresentable().dismiss(animated: true) {
|
||||
self.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh UserVerification UserVerification
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc protocol UserVerificationCoordinatorBridgePresenterDelegate {
|
||||
func userVerificationCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: UserVerificationCoordinatorBridgePresenter)
|
||||
}
|
||||
|
||||
/// UserVerificationCoordinatorBridgePresenter enables to start UserVerificationCoordinator from a view controller.
|
||||
/// This bridge is used while waiting for global usage of coordinator pattern.
|
||||
@objcMembers
|
||||
final class UserVerificationCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let presenter: Presentable
|
||||
private let session: MXSession
|
||||
private let userId: String
|
||||
private let userDisplayName: String?
|
||||
private var deviceId: String?
|
||||
|
||||
private var coordinator: Coordinator?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: UserVerificationCoordinatorBridgePresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(presenter: UIViewController, session: MXSession, userId: String, userDisplayName: String?) {
|
||||
self.presenter = presenter
|
||||
self.session = session
|
||||
self.userId = userId
|
||||
self.userDisplayName = userDisplayName
|
||||
super.init()
|
||||
}
|
||||
|
||||
init(presenter: UIViewController, session: MXSession, userId: String, userDisplayName: String?, deviceId: String) {
|
||||
self.presenter = presenter
|
||||
self.session = session
|
||||
self.userId = userId
|
||||
self.userDisplayName = userDisplayName
|
||||
self.deviceId = deviceId
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
self.present()
|
||||
}
|
||||
|
||||
func present() {
|
||||
|
||||
let userVerificationCoordinator: UserVerificationCoordinator
|
||||
|
||||
if let deviceId = self.deviceId {
|
||||
userVerificationCoordinator = UserVerificationCoordinator(presenter: self.presenter, session: self.session, userId: self.userId, userDisplayName: self.userDisplayName, deviceId: deviceId)
|
||||
} else {
|
||||
userVerificationCoordinator = UserVerificationCoordinator(presenter: self.presenter, session: self.session, userId: self.userId, userDisplayName: self.userDisplayName)
|
||||
}
|
||||
|
||||
userVerificationCoordinator.start()
|
||||
self.coordinator = userVerificationCoordinator
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh UserVerification UserVerification
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol UserVerificationCoordinatorDelegate: class {
|
||||
func userVerificationCoordinatorDidComplete(_ coordinator: UserVerificationCoordinatorType)
|
||||
}
|
||||
|
||||
/// `UserVerificationCoordinatorType` is a protocol describing a Coordinator that handle user verification navigation flow.
|
||||
protocol UserVerificationCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: UserVerificationCoordinatorDelegate? { get }
|
||||
}
|
||||
Reference in New Issue
Block a user