Refactor DeviceVerification prefix classes to KeyVerification where needed. And move key verification related classes into the same module KeyVerification.

This commit is contained in:
SBiOSoftWhare
2020-03-18 17:39:26 +01:00
parent 732ae10eb4
commit 25e8f7fccf
91 changed files with 819 additions and 803 deletions
@@ -0,0 +1,71 @@
// File created from ScreenTemplate
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
/*
Copyright 2019 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 KeyVerificationVerifyBySASCoordinator: KeyVerificationVerifyBySASCoordinatorType {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private var keyVerificationVerifyViewModel: KeyVerificationVerifyBySASViewModelType
private let keyVerificationVerifyViewController: KeyVerificationVerifyBySASViewController
// MARK: Public
// Must be used only internally
var childCoordinators: [Coordinator] = []
weak var delegate: KeyVerificationVerifyBySASCoordinatorDelegate?
// MARK: - Setup
init(session: MXSession, transaction: MXSASTransaction, verificationKind: KeyVerificationKind) {
self.session = session
let keyVerificationVerifyViewModel = KeyVerificationVerifyBySASViewModel(session: self.session, transaction: transaction, verificationKind: verificationKind)
let keyVerificationVerifyViewController = KeyVerificationVerifyBySASViewController.instantiate(with: keyVerificationVerifyViewModel)
self.keyVerificationVerifyViewModel = keyVerificationVerifyViewModel
self.keyVerificationVerifyViewController = keyVerificationVerifyViewController
}
// MARK: - Public methods
func start() {
self.keyVerificationVerifyViewModel.coordinatorDelegate = self
}
func toPresentable() -> UIViewController {
return self.keyVerificationVerifyViewController
}
}
// MARK: - DeviceVerificationVerifyViewModelCoordinatorDelegate
extension KeyVerificationVerifyBySASCoordinator: KeyVerificationVerifyBySASViewModelCoordinatorDelegate {
func keyVerificationVerifyViewModelDidComplete(_ viewModel: KeyVerificationVerifyBySASViewModelType) {
self.delegate?.keyVerificationVerifyBySASCoordinatorDidComplete(self)
}
func keyVerificationVerifyViewModelDidCancel(_ viewModel: KeyVerificationVerifyBySASViewModelType) {
self.delegate?.keyVerificationVerifyBySASCoordinatorDidCancel(self)
}
}
@@ -0,0 +1,29 @@
// File created from ScreenTemplate
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
/*
Copyright 2019 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 KeyVerificationVerifyBySASCoordinatorDelegate: class {
func keyVerificationVerifyBySASCoordinatorDidComplete(_ coordinator: KeyVerificationVerifyBySASCoordinatorType)
func keyVerificationVerifyBySASCoordinatorDidCancel(_ coordinator: KeyVerificationVerifyBySASCoordinatorType)
}
/// `KeyVerificationVerifyBySASCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
protocol KeyVerificationVerifyBySASCoordinatorType: Coordinator, Presentable {
var delegate: KeyVerificationVerifyBySASCoordinatorDelegate? { get }
}
@@ -0,0 +1,27 @@
// File created from ScreenTemplate
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
/*
Copyright 2019 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
/// KeyVerificationVerifyBySASViewController view actions exposed to view model
enum KeyVerificationVerifyBySASViewAction {
case loadData
case confirm
case complete
case cancel
}
@@ -0,0 +1,213 @@
<?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>
<!--Key Verification Verify BySAS View Controller-->
<scene sceneID="mt5-wz-YKA">
<objects>
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="KeyVerificationVerifyBySASViewController" 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>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9U2-KL-ZVA">
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="e7g-um-WO4">
<rect key="frame" x="0.0" y="0.0" width="375" height="485"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="voD-3Q-ryt">
<rect key="frame" x="0.0" y="0.0" width="375" height="485"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Verify this device by confirming the following emoji appear on the screen of the partner" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="he8-pl-xE9">
<rect key="frame" x="20" y="35" width="335" height="61"/>
<constraints>
<constraint firstAttribute="height" constant="61" id="Nam-ca-50k"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="For maximum security, we recommend you do this in person or use another trusted means of communication." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bxI-mu-qng">
<rect key="frame" x="20" y="115" width="335" height="80"/>
<constraints>
<constraint firstAttribute="height" constant="80" id="3ag-pn-F2b"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="3152 3307 8179" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="RD6-ue-X5c">
<rect key="frame" x="37.5" y="290.5" width="300" height="29"/>
<constraints>
<constraint firstAttribute="width" constant="300" id="6Q5-M2-ifj"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="24"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DtR-jx-UKY">
<rect key="frame" x="0.0" y="415" width="375" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DOt-5E-FjF">
<rect key="frame" x="156.5" y="10" width="62" height="30"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<state key="normal" title="Continue">
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
</state>
<connections>
<action selector="continueButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="uvI-tt-Nfj"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="DOt-5E-FjF" firstAttribute="centerY" secondItem="DtR-jx-UKY" secondAttribute="centerY" id="5eX-a5-zpP"/>
<constraint firstItem="DOt-5E-FjF" firstAttribute="centerX" secondItem="DtR-jx-UKY" secondAttribute="centerX" id="6v9-MN-mk2"/>
<constraint firstAttribute="height" constant="50" id="QNq-au-ZdL"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Waiting for partner to confirm..." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6yX-xD-4X5">
<rect key="frame" x="20" y="242" width="335" height="54"/>
<constraints>
<constraint firstAttribute="width" constant="335" id="9C0-ev-AVw"/>
<constraint firstAttribute="height" constant="54" id="pta-eP-0yH"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="34i-3X-YvQ">
<rect key="frame" x="0.0" y="215" width="375" height="180"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="180" id="kpT-ty-CDI"/>
</constraints>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="0.0" id="mHm-Tg-xbO">
<size key="itemSize" width="80" height="80"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="VerifyEmojiCollectionViewCell" id="iG2-Pq-pYr" customClass="VerifyEmojiCollectionViewCell" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="🐶" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5Cy-oI-To3">
<rect key="frame" x="0.0" y="6" width="80" height="43"/>
<fontDescription key="fontDescription" type="system" pointSize="36"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="dog" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="axP-53-KGQ">
<rect key="frame" x="0.0" y="58" width="80" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</view>
<constraints>
<constraint firstAttribute="trailing" secondItem="5Cy-oI-To3" secondAttribute="trailing" id="0vM-mM-9UI"/>
<constraint firstItem="axP-53-KGQ" firstAttribute="leading" secondItem="iG2-Pq-pYr" secondAttribute="leading" id="LeM-oa-STt"/>
<constraint firstItem="5Cy-oI-To3" firstAttribute="top" secondItem="iG2-Pq-pYr" secondAttribute="top" constant="6" id="ZBA-TS-ThT"/>
<constraint firstItem="5Cy-oI-To3" firstAttribute="leading" secondItem="iG2-Pq-pYr" secondAttribute="leading" id="hZz-hI-6k2"/>
<constraint firstAttribute="bottom" secondItem="axP-53-KGQ" secondAttribute="bottom" constant="6" id="ja0-Lm-Kej"/>
<constraint firstAttribute="trailing" secondItem="axP-53-KGQ" secondAttribute="trailing" id="yur-4a-SMc"/>
</constraints>
<connections>
<outlet property="emoji" destination="5Cy-oI-To3" id="pOP-P0-x8a"/>
<outlet property="name" destination="axP-53-KGQ" id="JYh-RY-edf"/>
</connections>
</collectionViewCell>
</cells>
<connections>
<outlet property="dataSource" destination="V8j-Lb-PgC" id="3m4-oH-KIj"/>
</connections>
</collectionView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="6yX-xD-4X5" firstAttribute="top" secondItem="bxI-mu-qng" secondAttribute="bottom" constant="47" id="6Bh-e4-HVr"/>
<constraint firstItem="34i-3X-YvQ" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" id="Bb1-EE-rq0"/>
<constraint firstAttribute="bottom" secondItem="DtR-jx-UKY" secondAttribute="bottom" constant="20" id="Cnb-UW-hYt"/>
<constraint firstAttribute="trailing" secondItem="DtR-jx-UKY" secondAttribute="trailing" id="K7y-Df-dgz"/>
<constraint firstItem="he8-pl-xE9" firstAttribute="leading" secondItem="bxI-mu-qng" secondAttribute="leading" id="Q9n-7p-gHl"/>
<constraint firstItem="he8-pl-xE9" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" id="QSg-yz-aaB"/>
<constraint firstItem="6yX-xD-4X5" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="Rg4-jV-Nht"/>
<constraint firstItem="bxI-mu-qng" firstAttribute="top" secondItem="he8-pl-xE9" secondAttribute="top" constant="80" id="UL4-x0-oFT"/>
<constraint firstItem="he8-pl-xE9" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="ZP8-mV-RBh"/>
<constraint firstItem="he8-pl-xE9" firstAttribute="trailing" secondItem="bxI-mu-qng" secondAttribute="trailing" id="Zeg-U8-uis"/>
<constraint firstAttribute="width" priority="750" constant="375" id="glD-Sz-73O"/>
<constraint firstItem="RD6-ue-X5c" firstAttribute="centerY" secondItem="34i-3X-YvQ" secondAttribute="centerY" id="h8F-gc-do4"/>
<constraint firstItem="DtR-jx-UKY" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" id="hK0-aA-d9H"/>
<constraint firstAttribute="trailing" secondItem="34i-3X-YvQ" secondAttribute="trailing" id="hg6-kq-M6U"/>
<constraint firstItem="RD6-ue-X5c" firstAttribute="centerX" secondItem="34i-3X-YvQ" secondAttribute="centerX" id="lVE-w6-pIa"/>
<constraint firstItem="DtR-jx-UKY" firstAttribute="top" secondItem="34i-3X-YvQ" secondAttribute="bottom" constant="20" id="liF-Qn-tiw"/>
<constraint firstItem="34i-3X-YvQ" firstAttribute="top" secondItem="bxI-mu-qng" secondAttribute="bottom" constant="20" id="r7A-9g-Mmb"/>
<constraint firstItem="he8-pl-xE9" firstAttribute="top" secondItem="voD-3Q-ryt" secondAttribute="top" constant="35" id="s3k-Io-834"/>
</constraints>
<variation key="default">
<mask key="subviews">
<exclude reference="6yX-xD-4X5"/>
</mask>
</variation>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="voD-3Q-ryt" secondAttribute="bottom" id="63a-5e-ptU"/>
<constraint firstItem="voD-3Q-ryt" firstAttribute="centerX" secondItem="e7g-um-WO4" secondAttribute="centerX" id="P2G-mq-gQW"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="voD-3Q-ryt" secondAttribute="trailing" id="QgV-SO-5yf"/>
<constraint firstItem="voD-3Q-ryt" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="e7g-um-WO4" secondAttribute="leading" id="YPo-u1-PtT"/>
<constraint firstItem="voD-3Q-ryt" firstAttribute="top" secondItem="e7g-um-WO4" secondAttribute="top" id="rhQ-96-szL"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="e7g-um-WO4" secondAttribute="trailing" id="GyG-Fh-PME"/>
<constraint firstItem="e7g-um-WO4" firstAttribute="width" secondItem="9U2-KL-ZVA" secondAttribute="width" id="Ok2-WQ-Zgc"/>
<constraint firstAttribute="bottom" secondItem="e7g-um-WO4" secondAttribute="bottom" constant="70" id="Y46-NP-zAc"/>
<constraint firstItem="e7g-um-WO4" firstAttribute="leading" secondItem="9U2-KL-ZVA" secondAttribute="leading" id="aoV-Yh-AcD"/>
<constraint firstItem="e7g-um-WO4" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="pFN-bA-SHw"/>
</constraints>
</scrollView>
</subviews>
<color key="backgroundColor" red="0.94509803920000002" green="0.96078431369999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="9U2-KL-ZVA" secondAttribute="bottom" id="7Cb-nY-CsO"/>
<constraint firstItem="9U2-KL-ZVA" firstAttribute="leading" secondItem="bFg-jh-JZB" secondAttribute="leading" id="GdQ-hK-muG"/>
<constraint firstItem="bFg-jh-JZB" firstAttribute="trailing" secondItem="9U2-KL-ZVA" secondAttribute="trailing" id="sbD-ek-vGJ"/>
<constraint firstItem="bFg-jh-JZB" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="wTB-V6-IHV"/>
</constraints>
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
</view>
<connections>
<outlet property="continueButton" destination="DOt-5E-FjF" id="ktw-U4-efQ"/>
<outlet property="continueButtonBackgroundView" destination="DtR-jx-UKY" id="9yG-wP-u8A"/>
<outlet property="decimalLabel" destination="RD6-ue-X5c" id="wlK-nl-CB6"/>
<outlet property="emojisCollectionView" destination="34i-3X-YvQ" id="wDE-oG-peo"/>
<outlet property="informationLabel" destination="bxI-mu-qng" id="pbX-aZ-inC"/>
<outlet property="scrollView" destination="9U2-KL-ZVA" id="ojG-2y-X7b"/>
<outlet property="titleLabel" destination="he8-pl-xE9" id="btA-kv-E2B"/>
<outlet property="waitingPartnerLabel" destination="6yX-xD-4X5" id="fre-bc-Kma"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-3199.1999999999998" y="-647.22638680659679"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,256 @@
// File created from ScreenTemplate
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
/*
Copyright 2019 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 KeyVerificationVerifyBySASViewController: UIViewController {
// MARK: - Constants
// MARK: - Properties
// MARK: Outlets
@IBOutlet private weak var scrollView: UIScrollView!
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var informationLabel: UILabel!
@IBOutlet private weak var decimalLabel: UILabel!
@IBOutlet private weak var emojisCollectionView: UICollectionView!
@IBOutlet private weak var waitingPartnerLabel: UILabel!
@IBOutlet private weak var continueButtonBackgroundView: UIView!
@IBOutlet private weak var continueButton: UIButton!
// MARK: Private
private var viewModel: KeyVerificationVerifyBySASViewModelType!
private var theme: Theme!
private var errorPresenter: MXKErrorPresentation!
private var activityPresenter: ActivityIndicatorPresenter!
// MARK: - Setup
class func instantiate(with viewModel: KeyVerificationVerifyBySASViewModelType) -> KeyVerificationVerifyBySASViewController {
let viewController = StoryboardScene.KeyVerificationVerifyBySASViewController.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.vc_removeBackTitle()
self.setupViews()
self.errorPresenter = MXKErrorAlertPresentation()
self.activityPresenter = ActivityIndicatorPresenter()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
self.viewModel.viewDelegate = self
self.viewModel.process(viewAction: .loadData)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Hide back button
self.navigationItem.setHidesBackButton(true, animated: animated)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return self.theme.statusBarStyle
}
// 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.titleLabel.textColor = theme.textPrimaryColor
self.informationLabel.textColor = theme.textPrimaryColor
self.decimalLabel.textColor = theme.textPrimaryColor
self.waitingPartnerLabel.textColor = theme.textPrimaryColor
self.continueButtonBackgroundView.backgroundColor = theme.backgroundColor
theme.applyStyle(onButton: self.continueButton)
emojisCollectionView.reloadData()
}
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.scrollView.keyboardDismissMode = .interactive
let isVerificationByEmoji = viewModel.emojis != nil
if isVerificationByEmoji {
self.decimalLabel.isHidden = true
} else {
self.emojisCollectionView.isHidden = true
self.decimalLabel.text = self.viewModel.decimal
}
let title: String
let instructionText: String
let adviceText: String
switch viewModel.verificationKind {
case .device:
title = VectorL10n.deviceVerificationTitle
instructionText = isVerificationByEmoji ? VectorL10n.deviceVerificationVerifyTitleEmoji : VectorL10n.deviceVerificationVerifyTitleNumber
adviceText = VectorL10n.deviceVerificationSecurityAdvice
case .user:
title = VectorL10n.keyVerificationUserTitle
instructionText = isVerificationByEmoji ? VectorL10n.keyVerificationVerifyUserTitleEmoji : VectorL10n.keyVerificationVerifyUserTitleNumber
adviceText = VectorL10n.deviceVerificationSecurityAdvice
}
self.title = title
self.titleLabel.text = instructionText
self.informationLabel.text = adviceText
self.waitingPartnerLabel.text = VectorL10n.deviceVerificationVerifyWaitPartner
self.waitingPartnerLabel.isHidden = true
self.continueButton.setTitle(VectorL10n.continue, for: .normal)
}
private func render(viewState: KeyVerificationVerifyViewState) {
switch viewState {
case .loading:
self.renderLoading()
case .loaded:
self.renderVerified()
case .cancelled(let reason):
self.renderCancelled(reason: reason)
case .cancelledByMe(let reason):
self.renderCancelledByMe(reason: reason)
case .error(let error):
self.render(error: error)
}
}
private func renderLoading() {
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
}
private func renderVerified() {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.continueButtonBackgroundView.isHidden = true
self.waitingPartnerLabel.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 render(error: Error) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
}
// MARK: - Actions
@IBAction private func continueButtonAction(_ sender: Any) {
self.viewModel.process(viewAction: .confirm)
}
private func cancelButtonAction() {
self.viewModel.process(viewAction: .cancel)
}
}
// MARK: - DeviceVerificationVerifyViewModelViewDelegate
extension KeyVerificationVerifyBySASViewController: KeyVerificationVerifyBySASViewModelViewDelegate {
func keyVerificationVerifyBySASViewModel(_ viewModel: KeyVerificationVerifyBySASViewModelType, didUpdateViewState viewSate: KeyVerificationVerifyViewState) {
self.render(viewState: viewSate)
}
}
extension KeyVerificationVerifyBySASViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
guard let emojis = self.viewModel.emojis else {
return 0
}
return emojis.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(for: indexPath, cellType: VerifyEmojiCollectionViewCell.self)
guard let emoji = self.viewModel.emojis?[indexPath.row] else {
return UICollectionViewCell()
}
cell.emoji.text = emoji.emoji
cell.name.text = VectorL10n.tr("Vector", "device_verification_emoji_\(emoji.name)")
cell.update(theme: self.theme)
return cell
}
}
@@ -0,0 +1,126 @@
// File created from ScreenTemplate
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
/*
Copyright 2019 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
final class KeyVerificationVerifyBySASViewModel: KeyVerificationVerifyBySASViewModelType {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private let transaction: MXSASTransaction
// MARK: Public
weak var viewDelegate: KeyVerificationVerifyBySASViewModelViewDelegate?
weak var coordinatorDelegate: KeyVerificationVerifyBySASViewModelCoordinatorDelegate?
let emojis: [MXEmojiRepresentation]?
let decimal: String?
let verificationKind: KeyVerificationKind
// MARK: - Setup
init(session: MXSession, transaction: MXSASTransaction, verificationKind: KeyVerificationKind) {
self.session = session
self.transaction = transaction
self.emojis = self.transaction.sasEmoji
self.decimal = self.transaction.sasDecimal
self.verificationKind = verificationKind
}
deinit {
}
// MARK: - Public
func process(viewAction: KeyVerificationVerifyBySASViewAction) {
switch viewAction {
case .loadData:
self.registerTransactionDidStateChangeNotification(transaction: transaction)
case .confirm:
self.confirmTransaction()
case .complete:
self.coordinatorDelegate?.keyVerificationVerifyViewModelDidComplete(self)
case .cancel:
self.cancelTransaction()
self.coordinatorDelegate?.keyVerificationVerifyViewModelDidCancel(self)
}
}
// MARK: - Private
private func confirmTransaction() {
self.update(viewState: .loading)
self.transaction.confirmSASMatch()
}
private func cancelTransaction() {
self.transaction.cancel(with: MXTransactionCancelCode.user())
}
private func update(viewState: KeyVerificationVerifyViewState) {
self.viewDelegate?.keyVerificationVerifyBySASViewModel(self, didUpdateViewState: viewState)
}
// MARK: - MXKeyVerificationTransactionDidChange
private func registerTransactionDidStateChangeNotification(transaction: MXSASTransaction) {
NotificationCenter.default.addObserver(self, selector: #selector(transactionDidStateChange(notification:)), name: NSNotification.Name.MXKeyVerificationTransactionDidChange, object: transaction)
}
private func unregisterTransactionDidStateChangeNotification() {
NotificationCenter.default.removeObserver(self, name: .MXKeyVerificationTransactionDidChange, object: nil)
}
@objc private func transactionDidStateChange(notification: Notification) {
guard let transaction = notification.object as? MXSASTransaction else {
return
}
switch transaction.state {
case MXSASTransactionStateVerified:
self.unregisterTransactionDidStateChangeNotification()
self.update(viewState: .loaded)
self.coordinatorDelegate?.keyVerificationVerifyViewModelDidComplete(self)
case MXSASTransactionStateCancelled:
guard let reason = transaction.reasonCancelCode else {
return
}
self.unregisterTransactionDidStateChangeNotification()
self.update(viewState: .cancelled(reason))
case MXSASTransactionStateError:
guard let error = transaction.error else {
return
}
self.unregisterTransactionDidStateChangeNotification()
self.update(viewState: .error(error))
case MXSASTransactionStateCancelledByMe:
guard let reason = transaction.reasonCancelCode else {
return
}
self.unregisterTransactionDidStateChangeNotification()
self.update(viewState: .cancelledByMe(reason))
default:
break
}
}
}
@@ -0,0 +1,41 @@
// File created from ScreenTemplate
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
/*
Copyright 2019 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 KeyVerificationVerifyBySASViewModelViewDelegate: class {
func keyVerificationVerifyBySASViewModel(_ viewModel: KeyVerificationVerifyBySASViewModelType, didUpdateViewState viewSate: KeyVerificationVerifyViewState)
}
protocol KeyVerificationVerifyBySASViewModelCoordinatorDelegate: class {
func keyVerificationVerifyViewModelDidComplete(_ viewModel: KeyVerificationVerifyBySASViewModelType)
func keyVerificationVerifyViewModelDidCancel(_ viewModel: KeyVerificationVerifyBySASViewModelType)
}
/// Protocol describing the view model used by `KeyVerificationVerifyBySASViewController`
protocol KeyVerificationVerifyBySASViewModelType {
var viewDelegate: KeyVerificationVerifyBySASViewModelViewDelegate? { get set }
var coordinatorDelegate: KeyVerificationVerifyBySASViewModelCoordinatorDelegate? { get set }
func process(viewAction: KeyVerificationVerifyBySASViewAction)
var emojis: [MXEmojiRepresentation]? { get }
var decimal: String? { get }
var verificationKind: KeyVerificationKind { get }
}
@@ -0,0 +1,28 @@
// File created from ScreenTemplate
// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify
/*
Copyright 2019 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
/// KeyVerificationVerifyBySASViewController view state
enum KeyVerificationVerifyViewState {
case loading
case loaded // verified
case cancelled(MXTransactionCancelCode)
case cancelledByMe(MXTransactionCancelCode)
case error(Error)
}
@@ -0,0 +1,27 @@
/*
Copyright 2019 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
class VerifyEmojiCollectionViewCell: UICollectionViewCell, Reusable, Themable {
@IBOutlet weak var emoji: UILabel!
@IBOutlet weak var name: UILabel!
func update(theme: Theme) {
name.textColor = theme.textPrimaryColor
}
}