2020-01-30 16:41:51 +00:00
// F i l e c r e a t e d f r o m S c r e e n T e m p l a t e
// $ c r e a t e S c r e e n . s h S e s s i o n S t a t u s U s e r V e r i f i c a t i o n S e s s i o n S t a t u s
/*
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 : // w w w . a p a c h e . o r g / l i c e n s e s / L I C E N S E - 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: - C o n s t a n t s
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: - P r o p e r t i e s
// MARK: O u t l e t s
@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: P r i v a t e
private var viewModel : UserVerificationSessionStatusViewModelType !
private var theme : Theme !
private var keyboardAvoider : KeyboardAvoider ?
private var errorPresenter : MXKErrorPresentation !
private var activityPresenter : ActivityIndicatorPresenter !
// MARK: - S e t u p
class func instantiate ( with viewModel : UserVerificationSessionStatusViewModelType ) -> UserVerificationSessionStatusViewController {
let viewController = StoryboardScene . UserVerificationSessionStatusViewController . initialScene . instantiate ( )
viewController . viewModel = viewModel
viewController . theme = ThemeService . shared ( ) . theme
return viewController
}
// MARK: - L i f e c y c l e
override func viewDidLoad ( ) {
super . viewDidLoad ( )
// D o a n y a d d i t i o n a l s e t u p a f t e r l o a d i n g t h e v i e w .
self . setupViews ( )
2020-01-31 14:03:44 +00:00
self . vc_removeBackTitle ( )
2020-01-30 16:41:51 +00:00
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: - P r i v a t e
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
2020-01-31 16:46:03 +00:00
self . untrustedSessionContainerView . isHidden = viewData . isDeviceTrusted
2020-01-30 16:41:51 +00:00
if viewData . isDeviceTrusted {
badgeImage = Asset . Images . encryptionTrusted . image
title = " Trusted "
} else {
badgeImage = Asset . Images . encryptionWarning . image
title = " Warning "
}
let unstrustedInformationText : String
let verifyButtonTitle : String
if viewData . isCurrentUser {
unstrustedInformationText = " If you didn’ t sign in to this session, your account may be compromised. "
verifyButtonTitle = " Verify "
} else {
unstrustedInformationText = " Until this user trusts this device, messages sent to and from it are labelled with warnings. Alternatively, you can manually verify it. "
verifyButtonTitle = " Manually verify "
}
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 : " This session is trusted for secure messaging because you verified it: " , attributes : informationTextDefaultAttributes )
informationAttributedText . append ( informationAttributedStringPart1 )
} else {
let informationAttributedStringPart1 = NSAttributedString ( string : " This device is trusted for secure messaging because " , attributes : informationTextDefaultAttributes )
let informationAttributedStringPart2 = NSAttributedString ( string : userInfoText , attributes : informationTextBoldAttributes )
let informationAttributedStringPart3 = NSAttributedString ( string : " verified it: " , attributes : informationTextDefaultAttributes )
informationAttributedText . append ( informationAttributedStringPart1 )
informationAttributedText . append ( informationAttributedStringPart2 )
informationAttributedText . append ( informationAttributedStringPart3 )
}
} else {
if viewData . isCurrentUser {
let informationAttributedStringPart1 = NSAttributedString ( string : " Verify this session to mark it as trusted & grant it access to encrypted messages: " , attributes : informationTextDefaultAttributes )
informationAttributedText . append ( informationAttributedStringPart1 )
} else {
let informationAttributedStringPart1 = NSAttributedString ( string : userInfoText , attributes : informationTextBoldAttributes )
let informationAttributedStringPart2 = NSAttributedString ( string : " signed in using a new device: " , 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: - A c t i o n s
@IBAction private func closeButtonAction ( _ sender : Any ) {
self . viewModel . process ( viewAction : . close )
}
@IBAction private func verifyButtonAction ( _ sender : Any ) {
self . viewModel . process ( viewAction : . verify )
}
}
// MARK: - U s e r V e r i f i c a t i o n S e s s i o n S t a t u s V i e w M o d e l V i e w D e l e g a t e
extension UserVerificationSessionStatusViewController : UserVerificationSessionStatusViewModelViewDelegate {
func userVerificationSessionStatusViewModel ( _ viewModel : UserVerificationSessionStatusViewModelType , didUpdateViewState viewSate : UserVerificationSessionStatusViewState ) {
self . render ( viewState : viewSate )
}
}