mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-22 01:22:46 +02:00
Refactoring secure backup references.
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
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
|
||||
|
||||
final class SecureKeyBackupSetupIntroCell: UIView, NibOwnerLoadable, Themable {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum ImageAlpha {
|
||||
static let highlighted: CGFloat = 0.3
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var backgroundView: UIView!
|
||||
@IBOutlet private weak var imageView: UIImageView!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
@IBOutlet private weak var accessoryImageView: UIImageView!
|
||||
@IBOutlet private weak var separatorView: UIView!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme?
|
||||
|
||||
private var isHighlighted: Bool = false {
|
||||
didSet {
|
||||
self.updateView()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var action: (() -> Void)?
|
||||
|
||||
// MARK: Setup
|
||||
|
||||
private func commonInit() {
|
||||
self.setupGestureRecognizer()
|
||||
|
||||
let accessoryTemplateImage = Asset.Images.disclosureIcon.image.withRenderingMode(.alwaysTemplate)
|
||||
self.accessoryImageView.image = accessoryTemplateImage
|
||||
self.accessoryImageView.highlightedImage = accessoryTemplateImage.vc_withAlpha(ImageAlpha.highlighted)
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(frame: CGRect.zero)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.loadNibContent()
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.loadNibContent()
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.backgroundView.backgroundColor = theme.backgroundColor
|
||||
self.imageView.tintColor = theme.textPrimaryColor
|
||||
self.titleLabel.textColor = theme.tintColor
|
||||
self.informationLabel.textColor = theme.textSecondaryColor
|
||||
self.accessoryImageView.tintColor = theme.textSecondaryColor
|
||||
self.separatorView.backgroundColor = theme.lineBreakColor
|
||||
|
||||
self.updateView()
|
||||
}
|
||||
|
||||
func fill(title: String, information: String, image: UIImage) {
|
||||
let templateImage = image.withRenderingMode(.alwaysTemplate)
|
||||
|
||||
self.imageView.image = templateImage
|
||||
self.imageView.highlightedImage = templateImage.vc_withAlpha(ImageAlpha.highlighted)
|
||||
|
||||
self.titleLabel.text = title
|
||||
self.informationLabel.text = information
|
||||
|
||||
self.setupAccessibility(title: title, isEnabled: true)
|
||||
self.updateView()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupAccessibility(title: String, isEnabled: Bool) {
|
||||
self.isAccessibilityElement = true
|
||||
self.accessibilityLabel = title
|
||||
self.accessibilityTraits = .button
|
||||
if !isEnabled {
|
||||
self.accessibilityTraits.insert(.notEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
private func setupGestureRecognizer() {
|
||||
let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(buttonAction(_:)))
|
||||
gestureRecognizer.minimumPressDuration = 0
|
||||
self.addGestureRecognizer(gestureRecognizer)
|
||||
}
|
||||
|
||||
private func updateView() {
|
||||
|
||||
if let theme = self.theme {
|
||||
self.backgroundView.backgroundColor = self.isHighlighted ? theme.overlayBackgroundColor : theme.backgroundColor
|
||||
}
|
||||
|
||||
self.imageView.isHighlighted = self.isHighlighted
|
||||
self.accessoryImageView.isHighlighted = self.isHighlighted
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc private func buttonAction(_ sender: UILongPressGestureRecognizer) {
|
||||
|
||||
let isBackgroundViewTouched = sender.vc_isTouchingInside()
|
||||
|
||||
switch sender.state {
|
||||
case .began, .changed:
|
||||
self.isHighlighted = isBackgroundViewTouched
|
||||
case .ended:
|
||||
self.isHighlighted = false
|
||||
|
||||
if isBackgroundViewTouched {
|
||||
self.action?()
|
||||
}
|
||||
case .cancelled:
|
||||
self.isHighlighted = false
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?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" customClass="SecureKeyBackupSetupIntroCell" customModule="Riot" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="accessoryImageView" destination="N76-Fa-onz" id="nYX-r6-t7R"/>
|
||||
<outlet property="backgroundView" destination="WrK-Wq-nzz" id="kgB-Zb-g9N"/>
|
||||
<outlet property="imageView" destination="PfY-xb-Psn" id="qgq-7B-nSS"/>
|
||||
<outlet property="informationLabel" destination="q9X-wE-MwV" id="a4J-ZK-teU"/>
|
||||
<outlet property="separatorView" destination="eKS-Ze-zhB" id="TF5-27-XFD"/>
|
||||
<outlet property="titleLabel" destination="eGd-jn-myj" id="MyG-x1-33i"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="426" height="122"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="WrK-Wq-nzz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="426" height="122"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="secrets_setup_key" translatesAutoresizingMaskIntoConstraints="NO" id="PfY-xb-Psn">
|
||||
<rect key="frame" x="19" y="18" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="PfY-xb-Psn" secondAttribute="height" multiplier="1:1" id="71S-sH-VTt"/>
|
||||
<constraint firstAttribute="width" constant="24" id="Shz-Df-UoM"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="252" text="Use a Security Key" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eGd-jn-myj">
|
||||
<rect key="frame" x="63" y="20" width="317" height="19.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" red="0.01176470588" green="0.70196078429999997" blue="0.50588235290000005" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Generate a security key to store somewhere safe like a password manager or a safe." lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="q9X-wE-MwV">
|
||||
<rect key="frame" x="63" y="49.5" width="317" height="52.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="disclosure_icon" translatesAutoresizingMaskIntoConstraints="NO" id="N76-Fa-onz">
|
||||
<rect key="frame" x="400" y="55" width="6" height="12"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="6" id="23d-HN-WiD"/>
|
||||
<constraint firstAttribute="height" constant="12" id="4Yt-I6-2YL"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eKS-Ze-zhB" userLabel="separatorView">
|
||||
<rect key="frame" x="0.0" y="121" width="426" height="1"/>
|
||||
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="5hw-tO-Dw9"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="PfY-xb-Psn" firstAttribute="centerY" secondItem="eGd-jn-myj" secondAttribute="centerY" id="3Ha-I2-24v"/>
|
||||
<constraint firstAttribute="trailing" secondItem="N76-Fa-onz" secondAttribute="trailing" constant="20" id="9C0-mv-gaL"/>
|
||||
<constraint firstAttribute="trailing" secondItem="eKS-Ze-zhB" secondAttribute="trailing" id="BlW-zS-HoC"/>
|
||||
<constraint firstItem="N76-Fa-onz" firstAttribute="leading" secondItem="eGd-jn-myj" secondAttribute="trailing" constant="20" id="CKw-l3-vdY"/>
|
||||
<constraint firstItem="PfY-xb-Psn" firstAttribute="leading" secondItem="WrK-Wq-nzz" secondAttribute="leading" constant="19" id="NXM-42-myZ"/>
|
||||
<constraint firstItem="q9X-wE-MwV" firstAttribute="leading" secondItem="eGd-jn-myj" secondAttribute="leading" id="Ovd-Xd-S7O"/>
|
||||
<constraint firstAttribute="bottom" secondItem="q9X-wE-MwV" secondAttribute="bottom" constant="20" id="S9M-i1-KQ6"/>
|
||||
<constraint firstItem="q9X-wE-MwV" firstAttribute="trailing" secondItem="eGd-jn-myj" secondAttribute="trailing" id="YU0-XX-vAU"/>
|
||||
<constraint firstItem="eGd-jn-myj" firstAttribute="top" secondItem="WrK-Wq-nzz" secondAttribute="top" constant="20" id="cAQ-R0-bco"/>
|
||||
<constraint firstAttribute="bottom" secondItem="eKS-Ze-zhB" secondAttribute="bottom" id="cnI-2n-T2c"/>
|
||||
<constraint firstItem="q9X-wE-MwV" firstAttribute="top" secondItem="eGd-jn-myj" secondAttribute="bottom" constant="10" id="kYI-mK-UC1"/>
|
||||
<constraint firstItem="N76-Fa-onz" firstAttribute="centerY" secondItem="WrK-Wq-nzz" secondAttribute="centerY" id="rG1-iI-v3T"/>
|
||||
<constraint firstItem="eKS-Ze-zhB" firstAttribute="leading" secondItem="WrK-Wq-nzz" secondAttribute="leading" id="raL-7N-TKN"/>
|
||||
<constraint firstItem="eGd-jn-myj" firstAttribute="leading" secondItem="PfY-xb-Psn" secondAttribute="trailing" constant="20" id="zi9-PT-A2O"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="WrK-Wq-nzz" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="dAF-lS-uGD"/>
|
||||
<constraint firstItem="WrK-Wq-nzz" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="fAW-fz-tYP"/>
|
||||
<constraint firstAttribute="bottom" secondItem="WrK-Wq-nzz" secondAttribute="bottom" id="lU3-Jc-aUe"/>
|
||||
<constraint firstAttribute="trailing" secondItem="WrK-Wq-nzz" secondAttribute="trailing" id="lUt-K8-wV9"/>
|
||||
</constraints>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
||||
<point key="canvasLocation" x="146.37681159420291" y="-122.54464285714285"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="disclosure_icon" width="6" height="12"/>
|
||||
<image name="secrets_setup_key" width="48" height="48"/>
|
||||
</resources>
|
||||
</document>
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
<?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="LSX-Vb-DIu">
|
||||
<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>
|
||||
<scenes>
|
||||
<!--Secure Key Backup Setup Intro View Controller-->
|
||||
<scene sceneID="srP-wo-sRp">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="LSX-Vb-DIu" customClass="SecureKeyBackupSetupIntroViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="pmD-eJ-G3m">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mvC-lc-ylT">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="852"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Fub-g4-o4o">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="391"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="79w-lw-B8z">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="391"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Safe guard against losing access to encrypted messages & data by backing up encryption keys on your server." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DZf-di-JcI">
|
||||
<rect key="frame" x="20" y="30" width="374" height="54"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tul-b1-7gY">
|
||||
<rect key="frame" x="0.0" y="114" width="414" height="257"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="Jc9-fc-Vwh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="257"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="d77-sQ-Uio">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="1"/>
|
||||
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="Gwq-JQ-4Zj"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RQF-pm-g37" customClass="SecureKeyBackupSetupIntroCell" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1" width="414" height="128"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="128" placeholder="YES" id="dOF-Du-P3S"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Yps-bc-ruH" customClass="SecureKeyBackupSetupIntroCell" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="129" width="414" height="128"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="128" placeholder="YES" id="Gpk-nd-3G2"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="d77-sQ-Uio" firstAttribute="width" secondItem="Jc9-fc-Vwh" secondAttribute="width" id="S3Q-F0-nwW"/>
|
||||
<constraint firstItem="RQF-pm-g37" firstAttribute="width" secondItem="Jc9-fc-Vwh" secondAttribute="width" id="fZs-9s-jUW"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Jc9-fc-Vwh" firstAttribute="top" secondItem="tul-b1-7gY" secondAttribute="top" id="1Vr-Y3-t2r"/>
|
||||
<constraint firstItem="Jc9-fc-Vwh" firstAttribute="leading" secondItem="tul-b1-7gY" secondAttribute="leading" id="4op-QY-3vL"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Jc9-fc-Vwh" secondAttribute="bottom" id="FqW-Rc-K1T"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Jc9-fc-Vwh" secondAttribute="trailing" id="QcB-wY-lCT"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="tul-b1-7gY" secondAttribute="trailing" id="TFD-iw-7y4"/>
|
||||
<constraint firstItem="tul-b1-7gY" firstAttribute="top" secondItem="DZf-di-JcI" secondAttribute="bottom" constant="30" id="Ury-rf-0xS"/>
|
||||
<constraint firstAttribute="trailing" secondItem="DZf-di-JcI" secondAttribute="trailing" constant="20" id="bS3-Gc-DyD"/>
|
||||
<constraint firstAttribute="width" priority="750" constant="500" id="fuc-5G-HCd"/>
|
||||
<constraint firstItem="tul-b1-7gY" firstAttribute="leading" secondItem="79w-lw-B8z" secondAttribute="leading" id="gYj-LZ-esB"/>
|
||||
<constraint firstItem="DZf-di-JcI" firstAttribute="leading" secondItem="79w-lw-B8z" secondAttribute="leading" constant="20" id="nPo-3g-WcD"/>
|
||||
<constraint firstAttribute="bottom" secondItem="tul-b1-7gY" secondAttribute="bottom" constant="20" id="qYa-0L-wrp"/>
|
||||
<constraint firstItem="DZf-di-JcI" firstAttribute="top" secondItem="79w-lw-B8z" secondAttribute="top" constant="30" id="t80-aS-JzH"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="79w-lw-B8z" firstAttribute="centerX" secondItem="Fub-g4-o4o" secondAttribute="centerX" id="5Az-52-cbQ"/>
|
||||
<constraint firstItem="79w-lw-B8z" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Fub-g4-o4o" secondAttribute="leading" id="Aei-Nr-Wzb"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="79w-lw-B8z" secondAttribute="trailing" id="CBG-M0-Uqd"/>
|
||||
<constraint firstAttribute="bottom" secondItem="79w-lw-B8z" secondAttribute="bottom" id="JbI-yr-iot"/>
|
||||
<constraint firstItem="79w-lw-B8z" firstAttribute="top" secondItem="Fub-g4-o4o" secondAttribute="top" id="s6B-HO-iVd"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="Fub-g4-o4o" secondAttribute="trailing" id="N9B-1U-3j7"/>
|
||||
<constraint firstItem="Fub-g4-o4o" firstAttribute="width" secondItem="mvC-lc-ylT" secondAttribute="width" id="NzH-5s-nuz"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Fub-g4-o4o" secondAttribute="bottom" id="gYj-V0-rlA"/>
|
||||
<constraint firstItem="Fub-g4-o4o" firstAttribute="leading" secondItem="mvC-lc-ylT" secondAttribute="leading" id="jHU-W6-8Ny"/>
|
||||
<constraint firstItem="Fub-g4-o4o" firstAttribute="top" secondItem="mvC-lc-ylT" secondAttribute="top" id="nFB-Af-IUw"/>
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="JkH-9M-Qql" firstAttribute="top" secondItem="mvC-lc-ylT" secondAttribute="top" id="5Vc-aR-jAF"/>
|
||||
<constraint firstItem="mvC-lc-ylT" firstAttribute="leading" secondItem="JkH-9M-Qql" secondAttribute="leading" id="HBB-YL-5P5"/>
|
||||
<constraint firstAttribute="bottom" secondItem="mvC-lc-ylT" secondAttribute="bottom" id="dn5-CG-87P"/>
|
||||
<constraint firstItem="JkH-9M-Qql" firstAttribute="trailing" secondItem="mvC-lc-ylT" secondAttribute="trailing" id="xj4-xF-VEA"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="JkH-9M-Qql"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="informationLabel" destination="DZf-di-JcI" id="czR-MT-Ipb"/>
|
||||
<outlet property="secureKeyCell" destination="RQF-pm-g37" id="5dK-h1-N6t"/>
|
||||
<outlet property="securePassphraseCell" destination="Yps-bc-ruH" id="56B-9H-MOh"/>
|
||||
<outlet property="topSeparatorView" destination="d77-sQ-Uio" id="TPp-kq-cFq"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="WpU-UP-qtu" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-3772.4637681159425" y="-774.10714285714278"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
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
|
||||
|
||||
protocol SecureKeyBackupSetupIntroViewControllerDelegate: class {
|
||||
func secureKeyBackupSetupIntroViewControllerDidTapUseKey(_ secureKeyBackupSetupIntroViewController: SecureKeyBackupSetupIntroViewController)
|
||||
func secureKeyBackupSetupIntroViewControllerDidTapUsePassphrase(_ secureKeyBackupSetupIntroViewController: SecureKeyBackupSetupIntroViewController)
|
||||
func secureKeyBackupSetupIntroViewControllerDidCancel(_ secureKeyBackupSetupIntroViewController: SecureKeyBackupSetupIntroViewController)
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
final class SecureKeyBackupSetupIntroViewController: UIViewController {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var topSeparatorView: UIView!
|
||||
@IBOutlet private weak var secureKeyCell: SecureKeyBackupSetupIntroCell!
|
||||
@IBOutlet private weak var securePassphraseCell: SecureKeyBackupSetupIntroCell!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme!
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: SecureKeyBackupSetupIntroViewControllerDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate() -> SecureKeyBackupSetupIntroViewController {
|
||||
let viewController = StoryboardScene.SecureKeyBackupSetupIntroViewController.initialScene.instantiate()
|
||||
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.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupViews() {
|
||||
let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.delegate?.secureKeyBackupSetupIntroViewControllerDidCancel(self)
|
||||
}
|
||||
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
|
||||
|
||||
self.title = VectorL10n.secureKeyBackupSetupIntroTitle
|
||||
|
||||
self.informationLabel.text = VectorL10n.secureKeyBackupSetupIntroInfo
|
||||
|
||||
self.secureKeyCell.fill(title: VectorL10n.secureKeyBackupSetupIntroUseSecurityKeyTitle,
|
||||
information: VectorL10n.secureKeyBackupSetupIntroUseSecurityKeyInfo,
|
||||
image: Asset.Images.secretsSetupKey.image)
|
||||
|
||||
self.secureKeyCell.action = { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.delegate?.secureKeyBackupSetupIntroViewControllerDidTapUseKey(self)
|
||||
}
|
||||
|
||||
self.securePassphraseCell.fill(title: VectorL10n.secureKeyBackupSetupIntroUseSecurityPassphraseTitle,
|
||||
information: VectorL10n.secureKeyBackupSetupIntroUseSecurityPassphraseInfo,
|
||||
image: Asset.Images.secretsSetupPassphrase.image)
|
||||
|
||||
self.securePassphraseCell.action = { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.delegate?.secureKeyBackupSetupIntroViewControllerDidTapUsePassphrase(self)
|
||||
}
|
||||
}
|
||||
|
||||
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.topSeparatorView.backgroundColor = theme.lineBreakColor
|
||||
self.secureKeyCell.update(theme: theme)
|
||||
self.securePassphraseCell.update(theme: theme)
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
}
|
||||
|
||||
@objc private func themeDidChange() {
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh KeyBackupSetup/SecureSetup SecureKeyBackupSetup
|
||||
/*
|
||||
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 SecureKeyBackupSetupCoordinator: SecureKeyBackupSetupCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let navigationRouter: NavigationRouterType
|
||||
private let recoveryService: MXRecoveryService
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: SecureKeyBackupSetupCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession) {
|
||||
self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController())
|
||||
self.recoveryService = session.crypto.recoveryService
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
let rootViewController = self.createIntro()
|
||||
self.navigationRouter.setRootModule(rootViewController)
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.navigationRouter.toPresentable()
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private func createIntro() -> SecureKeyBackupSetupIntroViewController {
|
||||
let introViewController = SecureKeyBackupSetupIntroViewController.instantiate()
|
||||
introViewController.delegate = self
|
||||
return introViewController
|
||||
}
|
||||
|
||||
private func showSetupKey(passphrase: String? = nil) {
|
||||
let coordinator = SecretsSetupRecoveryKeyCoordinator(recoveryService: self.recoveryService, passphrase: passphrase)
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
|
||||
self.add(childCoordinator: coordinator)
|
||||
self.navigationRouter.push(coordinator, animated: true) { [weak self] in
|
||||
self?.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
private func showSetupPassphrase() {
|
||||
let coordinator = SecretsSetupRecoveryPassphraseCoordinator(passphraseInput: .new)
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
|
||||
self.add(childCoordinator: coordinator)
|
||||
self.navigationRouter.push(coordinator, animated: true) { [weak self] in
|
||||
self?.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
private func showSetupPassphraseConfirmation(with passphrase: String) {
|
||||
let coordinator = SecretsSetupRecoveryPassphraseCoordinator(passphraseInput: .confirm(passphrase))
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
|
||||
self.add(childCoordinator: coordinator)
|
||||
self.navigationRouter.push(coordinator, animated: true) { [weak self] in
|
||||
self?.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
private func showCancelAlert() {
|
||||
let alertController = UIAlertController(title: VectorL10n.secureKeyBackupSetupCancelAlertTitle,
|
||||
message: VectorL10n.secureKeyBackupSetupCancelAlertMessage,
|
||||
preferredStyle: .alert)
|
||||
|
||||
alertController.addAction(UIAlertAction(title: VectorL10n.continue, style: .cancel, handler: { action in
|
||||
}))
|
||||
|
||||
alertController.addAction(UIAlertAction(title: VectorL10n.keyBackupSetupSkipAlertSkipAction, style: .default, handler: { action in
|
||||
self.delegate?.secureKeyBackupSetupCoordinatorDidCancel(self)
|
||||
}))
|
||||
|
||||
self.navigationRouter.present(alertController, animated: true)
|
||||
}
|
||||
|
||||
private func didCancel(showSkipAlert: Bool = true) {
|
||||
if showSkipAlert {
|
||||
self.showCancelAlert()
|
||||
} else {
|
||||
self.delegate?.secureKeyBackupSetupCoordinatorDidCancel(self)
|
||||
}
|
||||
}
|
||||
|
||||
private func didComplete() {
|
||||
self.delegate?.secureKeyBackupSetupCoordinatorDidComplete(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SecureKeyBackupSetupIntroViewControllerDelegate
|
||||
extension SecureKeyBackupSetupCoordinator: SecureKeyBackupSetupIntroViewControllerDelegate {
|
||||
|
||||
func secureKeyBackupSetupIntroViewControllerDidTapUseKey(_ secureKeyBackupSetupIntroViewController: SecureKeyBackupSetupIntroViewController) {
|
||||
self.showSetupKey()
|
||||
}
|
||||
|
||||
func secureKeyBackupSetupIntroViewControllerDidTapUsePassphrase(_ secureKeyBackupSetupIntroViewController: SecureKeyBackupSetupIntroViewController) {
|
||||
self.showSetupPassphrase()
|
||||
}
|
||||
|
||||
func secureKeyBackupSetupIntroViewControllerDidCancel(_ secureKeyBackupSetupIntroViewController: SecureKeyBackupSetupIntroViewController) {
|
||||
self.didCancel()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SecretsSetupRecoveryKeyCoordinatorDelegate
|
||||
extension SecureKeyBackupSetupCoordinator: SecretsSetupRecoveryKeyCoordinatorDelegate {
|
||||
|
||||
func secretsSetupRecoveryKeyCoordinatorDidComplete(_ coordinator: SecretsSetupRecoveryKeyCoordinatorType) {
|
||||
self.didComplete()
|
||||
}
|
||||
|
||||
func secretsSetupRecoveryKeyCoordinatorDidFailed(_ coordinator: SecretsSetupRecoveryKeyCoordinatorType) {
|
||||
self.didCancel(showSkipAlert: false)
|
||||
}
|
||||
|
||||
func secretsSetupRecoveryKeyCoordinatorDidCancel(_ coordinator: SecretsSetupRecoveryKeyCoordinatorType) {
|
||||
self.didCancel()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SecretsSetupRecoveryPassphraseCoordinatorDelegate
|
||||
extension SecureKeyBackupSetupCoordinator: SecretsSetupRecoveryPassphraseCoordinatorDelegate {
|
||||
|
||||
func secretsSetupRecoveryPassphraseCoordinator(_ coordinator: SecretsSetupRecoveryPassphraseCoordinatorType, didEnterNewPassphrase passphrase: String) {
|
||||
self.showSetupPassphraseConfirmation(with: passphrase)
|
||||
}
|
||||
|
||||
func secretsSetupRecoveryPassphraseCoordinator(_ coordinator: SecretsSetupRecoveryPassphraseCoordinatorType, didConfirmPassphrase passphrase: String) {
|
||||
self.showSetupKey(passphrase: passphrase)
|
||||
}
|
||||
|
||||
func secretsSetupRecoveryPassphraseCoordinatorDidCancel(_ coordinator: SecretsSetupRecoveryPassphraseCoordinatorType) {
|
||||
self.didCancel()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh KeyBackupSetup/SecureSetup SecureKeyBackupSetup
|
||||
/*
|
||||
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 SecureKeyBackupSetupCoordinatorBridgePresenterDelegate {
|
||||
func secureKeyBackupSetupCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: SecureKeyBackupSetupCoordinatorBridgePresenter)
|
||||
func secureKeyBackupSetupCoordinatorBridgePresenterDelegateDidCancel(_ coordinatorBridgePresenter: SecureKeyBackupSetupCoordinatorBridgePresenter)
|
||||
}
|
||||
|
||||
/// SecureKeyBackupSetupCoordinatorBridgePresenter enables to start SecureKeyBackupSetupCoordinator from a view controller.
|
||||
/// This bridge is used while waiting for global usage of coordinator pattern.
|
||||
@objcMembers
|
||||
final class SecureKeyBackupSetupCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private var coordinator: SecureKeyBackupSetupCoordinator?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: SecureKeyBackupSetupCoordinatorBridgePresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession) {
|
||||
self.session = session
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
// NOTE: Default value feature is not compatible with Objective-C.
|
||||
// func present(from viewController: UIViewController, animated: Bool) {
|
||||
// self.present(from: viewController, animated: animated)
|
||||
// }
|
||||
|
||||
func present(from viewController: UIViewController, animated: Bool) {
|
||||
let secureKeyBackupSetupCoordinator = SecureKeyBackupSetupCoordinator(session: self.session)
|
||||
secureKeyBackupSetupCoordinator.delegate = self
|
||||
viewController.present(secureKeyBackupSetupCoordinator.toPresentable(), animated: animated, completion: nil)
|
||||
secureKeyBackupSetupCoordinator.start()
|
||||
|
||||
self.coordinator = secureKeyBackupSetupCoordinator
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
coordinator.toPresentable().dismiss(animated: animated) {
|
||||
self.coordinator = nil
|
||||
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SecureKeyBackupSetupCoordinatorDelegate
|
||||
extension SecureKeyBackupSetupCoordinatorBridgePresenter: SecureKeyBackupSetupCoordinatorDelegate {
|
||||
func secureKeyBackupSetupCoordinatorDidComplete(_ coordinator: SecureKeyBackupSetupCoordinatorType) {
|
||||
self.delegate?.secureKeyBackupSetupCoordinatorBridgePresenterDelegateDidComplete(self)
|
||||
}
|
||||
|
||||
func secureKeyBackupSetupCoordinatorDidCancel(_ coordinator: SecureKeyBackupSetupCoordinatorType) {
|
||||
self.delegate?.secureKeyBackupSetupCoordinatorBridgePresenterDelegateDidCancel(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh KeyBackupSetup/SecureSetup SecureKeyBackupSetup
|
||||
/*
|
||||
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 SecureKeyBackupSetupCoordinatorDelegate: class {
|
||||
func secureKeyBackupSetupCoordinatorDidComplete(_ coordinator: SecureKeyBackupSetupCoordinatorType)
|
||||
func secureKeyBackupSetupCoordinatorDidCancel(_ coordinator: SecureKeyBackupSetupCoordinatorType)
|
||||
}
|
||||
|
||||
/// `SecureKeyBackupSetupCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow.
|
||||
protocol SecureKeyBackupSetupCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: SecureKeyBackupSetupCoordinatorDelegate? { get }
|
||||
}
|
||||
Reference in New Issue
Block a user