merge default IOS-1363
authorAdam Kowalski <adam@pep-project.org>
Wed, 11 Mar 2020 19:09:12 +0100
branchIOS-1363
changeset 12213f631bb6e6e2f
parent 12212 376d13d6b73c
parent 12211 402fdfa0698c
child 12240 c726eef9fa87
merge default
     1.1 --- a/pEpForiOS.xcodeproj/project.pbxproj	Wed Mar 11 19:08:34 2020 +0100
     1.2 +++ b/pEpForiOS.xcodeproj/project.pbxproj	Wed Mar 11 19:09:12 2020 +0100
     1.3 @@ -2026,14 +2026,14 @@
     1.4  			path = HTMLParser;
     1.5  			sourceTree = "<group>";
     1.6  		};
     1.7 -		438D5A6F1EA77CFC001A37E1 /* Handshake */ = {
     1.8 +		438D5A6F1EA77CFC001A37E1 /* TrustManagement */ = {
     1.9  			isa = PBXGroup;
    1.10  			children = (
    1.11  				B70D32A6205BCC120094A92A /* CellsAndSections */,
    1.12  				5BC14AEF23EB19B5005F0AF3 /* TrustManagementViewController.swift */,
    1.13  				5BC14ABE23E2EE78005F0AF3 /* TrustManagementViewModel.swift */,
    1.14  			);
    1.15 -			path = Handshake;
    1.16 +			path = TrustManagement;
    1.17  			sourceTree = "<group>";
    1.18  		};
    1.19  		43980E1A1CBD0BC900A7FC3C = {
    1.20 @@ -2297,7 +2297,7 @@
    1.21  				B70D32B0205BCFBD0094A92A /* EmailDisplay */,
    1.22  				B78CF8261E76D70D008C1739 /* Filter */,
    1.23  				B71EBBB41E55E43100150177 /* Folder */,
    1.24 -				438D5A6F1EA77CFC001A37E1 /* Handshake */,
    1.25 +				438D5A6F1EA77CFC001A37E1 /* TrustManagement */,
    1.26  				B70D32AA205BCCC70094A92A /* Login */,
    1.27  				B70D32AF205BCF460094A92A /* ManualLogin */,
    1.28  				15BA537A20A1F5CA0090F126 /* MoveToFolder */,
     2.1 --- a/pEpForiOS/Base.lproj/TrustManagement.storyboard	Wed Mar 11 19:08:34 2020 +0100
     2.2 +++ b/pEpForiOS/Base.lproj/TrustManagement.storyboard	Wed Mar 11 19:09:12 2020 +0100
     2.3 @@ -615,7 +615,7 @@
     2.4                      </navigationItem>
     2.5                      <connections>
     2.6                          <outlet property="optionsButton" destination="zNZ-mq-ElZ" id="J6G-jk-mG0"/>
     2.7 -                        <outlet property="trustManagementTableView" destination="crl-YO-ia8" id="RKE-4e-rtw"/>
     2.8 +                        <outlet property="tableView" destination="crl-YO-ia8" id="hZ6-sT-Gcp"/>
     2.9                      </connections>
    2.10                  </viewController>
    2.11                  <placeholder placeholderIdentifier="IBFirstResponder" id="OAD-MK-tHj" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
     3.1 --- a/pEpForiOS/UI/Handshake/CellsAndSections/TrustManagementResetTableViewCell.swift	Wed Mar 11 19:08:34 2020 +0100
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,35 +0,0 @@
     3.4 -//
     3.5 -//  TrustManagementResetTableViewCell.swift
     3.6 -//  pEp
     3.7 -//
     3.8 -//  Created by Martin Brude on 17/02/2020.
     3.9 -//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    3.10 -//
    3.11 -
    3.12 -import UIKit
    3.13 -
    3.14 -final class TrustManagementResetTableViewCell: UITableViewCell {
    3.15 -
    3.16 -    @IBOutlet weak var partnerNameLabel: UILabel!
    3.17 -    @IBOutlet weak var partnerImageView: UIImageView!
    3.18 -    @IBOutlet weak var resetLabel: UILabel!
    3.19 -    @IBOutlet weak var resetButton: UIButton!
    3.20 -    
    3.21 -    weak var delegate : TrustManagementResetTableViewCellDelegate?
    3.22 -
    3.23 -    override func awakeFromNib() {
    3.24 -        super.awakeFromNib()
    3.25 -        
    3.26 -           //Reset Button
    3.27 -           resetButton.pEpIfyForTrust(backgroundColor: .pEpGrayBackgroundReset, textColor: .white)
    3.28 -        
    3.29 -           //Reset label
    3.30 -           resetLabel.text = NSLocalizedString("Reset all p≡p data for this comunication partner:",
    3.31 -                                               comment: "Reset all p≡p data for this comunication partner:")
    3.32 -
    3.33 -    }
    3.34 -
    3.35 -    @IBAction private func resetButtonPressed() {
    3.36 -        delegate?.resetButtonPressed(on: self)
    3.37 -    }
    3.38 -}
     4.1 --- a/pEpForiOS/UI/Handshake/CellsAndSections/TrustManagementTableViewCell.swift	Wed Mar 11 19:08:34 2020 +0100
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,104 +0,0 @@
     4.4 -//
     4.5 -//  HandshakePartnerTableViewCell.swift
     4.6 -//  pEp
     4.7 -//
     4.8 -//  Created by Martin Brude on 07/02/2020.
     4.9 -//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    4.10 -//
    4.11 -
    4.12 -import UIKit
    4.13 -
    4.14 -/// UITableViewCell for trust management screen
    4.15 -final class TrustManagementTableViewCell: UITableViewCell {
    4.16 - 
    4.17 -    //Content
    4.18 -    @IBOutlet weak var partnerImageView: UIImageView!
    4.19 -    @IBOutlet weak var privacyStatusImageView: UIImageView!
    4.20 -    @IBOutlet weak var partnerNameLabel: UILabel!
    4.21 -    @IBOutlet weak var privacyStatusLabel: UILabel!
    4.22 -    @IBOutlet weak var trustwordsLabel: UILabel!
    4.23 -    @IBOutlet weak var descriptionLabel: UILabel!
    4.24 -    @IBOutlet weak var languageButton: UIButton!
    4.25 -    
    4.26 -    //Only for i18n and layout
    4.27 -    @IBOutlet weak var resetLabel: UILabel!
    4.28 -    @IBOutlet weak var declineButton: UIButton!
    4.29 -    @IBOutlet weak var confirmButton: UIButton!
    4.30 -    @IBOutlet weak var resetButton: UIButton!
    4.31 -    
    4.32 -    //Hide these views in case pepColor is not yellow.
    4.33 -    @IBOutlet weak var trustwordsStackView: UIStackView!
    4.34 -    @IBOutlet weak var trustwordsButtonsContainer: UIView!
    4.35 -    
    4.36 -    weak var delegate : TrustManagementTableViewCellDelegate?
    4.37 -    
    4.38 -    override func awakeFromNib() {
    4.39 -        super.awakeFromNib()
    4.40 -        setupView()
    4.41 -    }
    4.42 -    
    4.43 -    /// Reset attributes of the cell that are not related to content.
    4.44 -    override func prepareForReuse() {
    4.45 -        super.prepareForReuse()
    4.46 -        removeGestureRecognizers()
    4.47 -    }
    4.48 -
    4.49 -    // MARK: - Actions
    4.50 -    
    4.51 -    @IBAction private func languageButtonPressed() {
    4.52 -        delegate?.languageButtonPressed(on: self)
    4.53 -    }
    4.54 -    
    4.55 -    @IBAction private func declineButtonPressed() {
    4.56 -        delegate?.declineButtonPressed(on: self)
    4.57 -    }
    4.58 -
    4.59 -    @IBAction private func confirmButtonPressed() {
    4.60 -        delegate?.confirmButtonPressed(on: self)
    4.61 -    }
    4.62 -
    4.63 -    @IBAction private func resetButtonPressed() {
    4.64 -        delegate?.resetButtonPressed(on: self)
    4.65 -    }
    4.66 -    
    4.67 -    @objc private func trustwordsLabelPressed() {
    4.68 -        delegate?.trustwordsLabelPressed(on: self)
    4.69 -    }
    4.70 -    
    4.71 -    // MARK: - Private
    4.72 -    
    4.73 -    /// Setup the view with the row data.
    4.74 -    private func setupView() {
    4.75 -        removeGestureRecognizers()
    4.76 -
    4.77 -        let gesture = UITapGestureRecognizer(target: self, action: #selector(trustwordsLabelPressed))
    4.78 -        trustwordsLabel.addGestureRecognizer(gesture)
    4.79 -    
    4.80 -        //Confirm Button
    4.81 -        let confirmTitle = NSLocalizedString("Confirm", comment: "Confirm correct trustwords/PGP fingerprint")
    4.82 -        confirmButton.setTitle(confirmTitle, for: .normal)
    4.83 -        confirmButton.pEpIfyForTrust(backgroundColor: .pEpGreen, textColor: .white)
    4.84 -        
    4.85 -        //Decline Button
    4.86 -        let declineTitle = NSLocalizedString("Decline", comment: "Incorrect trustwords/PGP fingerprint")
    4.87 -        declineButton.setTitle(declineTitle, for: .normal)
    4.88 -        declineButton.pEpIfyForTrust(backgroundColor: .pEpRed, textColor: .white)
    4.89 -
    4.90 -        //Reset Button
    4.91 -        resetButton.pEpIfyForTrust(backgroundColor: .pEpGrayBackgroundReset, textColor: .white)
    4.92 -     
    4.93 -        //Reset label
    4.94 -        resetLabel.text = NSLocalizedString("Reset all p≡p data for this comunication partner:",
    4.95 -                                            comment: "Reset all p≡p data for this comunication partner:")
    4.96 -        //Image view
    4.97 -        partnerImageView.layer.cornerRadius = 10
    4.98 -        partnerImageView.layer.masksToBounds = true
    4.99 -    }
   4.100 -
   4.101 -    private func removeGestureRecognizers() {
   4.102 -        let existingGRs = gestureRecognizers ?? []
   4.103 -        for gr in existingGRs {
   4.104 -            removeGestureRecognizer(gr)
   4.105 -        }
   4.106 -    }
   4.107 -}
     5.1 --- a/pEpForiOS/UI/Handshake/CellsAndSections/TrustManagementTableViewCellProtocols.swift	Wed Mar 11 19:08:34 2020 +0100
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,33 +0,0 @@
     5.4 -//
     5.5 -//  TrustManagementTableViewCellProtocols.swift
     5.6 -//  pEp
     5.7 -//
     5.8 -//  Created by Martin Brude on 18/02/2020.
     5.9 -//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    5.10 -//
    5.11 -
    5.12 -import Foundation
    5.13 -
    5.14 -/// Delegate to notify the events in the cell.
    5.15 -protocol TrustManagementResetTableViewCellDelegate: class {
    5.16 -    /// Delegate method to notify the reset button has been pressed.
    5.17 -    /// - Parameter cell: The cell where the reset button has been pressed
    5.18 -    func resetButtonPressed(on cell: UITableViewCell)
    5.19 -}
    5.20 -
    5.21 -/// Delegate to notify the events in the cell.
    5.22 -protocol TrustManagementTableViewCellDelegate: TrustManagementResetTableViewCellDelegate {
    5.23 -    
    5.24 -    /// Delegate method to notify the language button has been pressed.
    5.25 -    /// - Parameter cell: The cell where the language button has been pressed
    5.26 -    func languageButtonPressed(on cell: TrustManagementTableViewCell)
    5.27 -    /// Delegate method to notify the decline button has been pressed.
    5.28 -    /// - Parameter cell: The cell where the decline button has been pressed
    5.29 -    func declineButtonPressed(on cell: TrustManagementTableViewCell)
    5.30 -    /// Delegate method to notify the confirm button has been pressed.
    5.31 -    /// - Parameter cell: The cell where the confirm button has been pressed
    5.32 -    func confirmButtonPressed(on cell: TrustManagementTableViewCell)
    5.33 -    /// Delegate method to notify the trustwords label has been pressed.
    5.34 -    /// - Parameter cell: The cell where the trustwords label has been pressed
    5.35 -    func trustwordsLabelPressed(on cell : TrustManagementTableViewCell)
    5.36 -}
     6.1 --- a/pEpForiOS/UI/Handshake/TrustManagementViewController.swift	Wed Mar 11 19:08:34 2020 +0100
     6.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3 @@ -1,402 +0,0 @@
     6.4 -//
     6.5 -//  TrustManagementViewController.swift
     6.6 -//  pEp
     6.7 -//
     6.8 -//  Created by Martin Brude on 05/02/2020.
     6.9 -//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    6.10 -//
    6.11 -
    6.12 -import UIKit
    6.13 -
    6.14 -/// View Controller to handle the HandshakeView.
    6.15 -class TrustManagementViewController: BaseViewController {
    6.16 -    private let onlyMasterCellIdentifier = "TrustManagementTableViewCell_OnlyMaster"
    6.17 -    private let masterAndDetailCellIdentifier = "TrustManagementTableViewCell_Detailed"
    6.18 -    private let resetCellIdentifier = "TrustManagementTableViewResetCell"
    6.19 -    @IBOutlet weak var tableView: UITableView!
    6.20 -    @IBOutlet weak var optionsButton: UIBarButtonItem!
    6.21 -
    6.22 -    var viewModel : TrustManagementViewModel?
    6.23 -
    6.24 -    override func viewDidLoad() {
    6.25 -        super.viewDidLoad()
    6.26 -        setup()
    6.27 -        tableView.rowHeight = UITableView.automaticDimension
    6.28 -        tableView.estimatedRowHeight = 400
    6.29 -    }
    6.30 -
    6.31 -    override func viewWillAppear(_ animated: Bool) {
    6.32 -        super.viewWillAppear(animated)
    6.33 -        guard let vm = viewModel else {
    6.34 -            Log.shared.errorAndCrash("The viewModel must not be nil")
    6.35 -            return
    6.36 -        }
    6.37 -        if (!vm.shouldShowOptionsButton) {
    6.38 -            navigationItem.rightBarButtonItems?.removeAll(where: {$0 == optionsButton})
    6.39 -        } else {
    6.40 -            optionsButton.title = NSLocalizedString("Options", comment: "Options")
    6.41 -        }
    6.42 -        vm.delegate = self
    6.43 -    }
    6.44 -
    6.45 -    override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
    6.46 -        guard let vm = viewModel, vm.canUndo() && motion == .motionShake,
    6.47 -            let actionName = vm.lastActionPerformed() else { return }
    6.48 -        let title = NSLocalizedString("Undo \(actionName)", comment: "Undo trust change verification alert title")
    6.49 -        let alertController = UIAlertController.pEpAlertController(title: title,
    6.50 -                                                                   message: nil,
    6.51 -                                                                   preferredStyle: .alert)
    6.52 -        let confirmTitle = NSLocalizedString("Undo", comment: "Undo trust change verification button title")
    6.53 -        let action = UIAlertAction(title: confirmTitle, style: .default) { [weak vm] (action) in
    6.54 -            vm?.shakeMotionDidEnd()
    6.55 -        }
    6.56 -        alertController.addAction(action)
    6.57 -        
    6.58 -        //For the cancel button another action.
    6.59 -        let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel trust change to be undone")
    6.60 -        let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) { _ in
    6.61 -            alertController.dismiss(animated: true, completion: nil)
    6.62 -        }
    6.63 -        alertController.addAction(cancelAction)
    6.64 -        present(alertController, animated: true, completion: nil)
    6.65 -    }
    6.66 -
    6.67 -    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    6.68 -        super.viewWillTransition(to: size, with: coordinator)
    6.69 -        tableView.reloadData()
    6.70 -    }
    6.71 -
    6.72 -    @IBAction private func optionsButtonPressed(_ sender: UIBarButtonItem) {
    6.73 -        presentToogleProtectionActionSheet()
    6.74 -    }
    6.75 -   
    6.76 -    deinit {
    6.77 -        unregisterNotifications()
    6.78 -    }
    6.79 -}
    6.80 -
    6.81 -// MARK: - Private
    6.82 -
    6.83 -extension TrustManagementViewController {
    6.84 -
    6.85 -    private func setup() {
    6.86 -        registerForNotifications()
    6.87 -        setLeftBarButton()
    6.88 -    }
    6.89 -}
    6.90 -
    6.91 -/// MARK: - UITableViewDataSource
    6.92 -
    6.93 -extension TrustManagementViewController : UITableViewDataSource  {
    6.94 -    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    6.95 -        guard let numberOfRows = viewModel?.rows.count else {
    6.96 -            Log.shared.error("The viewModel must not be nil")
    6.97 -            return 0
    6.98 -        }
    6.99 -        return numberOfRows
   6.100 -    }
   6.101 -    
   6.102 -    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   6.103 -        guard let row = viewModel?.rows[indexPath.row] else {
   6.104 -            Log.shared.error("The row couldn't be dequeued")
   6.105 -            return UITableViewCell()
   6.106 -        }
   6.107 -        
   6.108 -        /// Cell for reset
   6.109 -        if row.color == .noColor,
   6.110 -            let cell = tableView.dequeueReusableCell(withIdentifier: resetCellIdentifier, for: indexPath)
   6.111 -                as? TrustManagementResetTableViewCell {
   6.112 -            cell.delegate = self
   6.113 -            cell.partnerNameLabel.text = row.name
   6.114 -            viewModel?.getImage(forRowAt: indexPath, complete: { (image) in
   6.115 -                DispatchQueue.main.async {
   6.116 -                    cell.partnerImageView.image = image
   6.117 -                }
   6.118 -            })
   6.119 -            return cell
   6.120 -        }
   6.121 -         
   6.122 -        /// Cell ´no-noColor´ context
   6.123 -        let identifier : String
   6.124 -        if UIDevice.current.orientation.isLandscape ||
   6.125 -            UIDevice.current.userInterfaceIdiom == .pad {
   6.126 -            identifier = masterAndDetailCellIdentifier
   6.127 -        } else {
   6.128 -            identifier = onlyMasterCellIdentifier
   6.129 -        }
   6.130 -
   6.131 -        guard let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
   6.132 -            as? TrustManagementTableViewCell else {
   6.133 -                Log.shared.error("The TrustManagementTableViewCell couldn't be dequeued")
   6.134 -                return UITableViewCell()
   6.135 -        }
   6.136 -        viewModel?.getImage(forRowAt: indexPath, complete: { (image) in
   6.137 -            DispatchQueue.main.async {
   6.138 -                cell.partnerImageView.image = image
   6.139 -            }
   6.140 -        })
   6.141 -        cell.privacyStatusImageView.image = row.privacyStatusImage
   6.142 -        cell.partnerNameLabel.text = row.name
   6.143 -        cell.privacyStatusLabel.text = row.privacyStatusName
   6.144 -        cell.descriptionLabel.text = row.description
   6.145 -        configureTrustwords(identifier, row, cell, indexPath)
   6.146 -        cell.delegate = self
   6.147 -        return cell
   6.148 -    }
   6.149 -}
   6.150 -
   6.151 -/// MARK: - UIAlertControllers
   6.152 -
   6.153 -extension TrustManagementViewController {
   6.154 -    
   6.155 -    /// This should only be used if the flow comes from the Compose View.
   6.156 -    private func presentToogleProtectionActionSheet() {
   6.157 -        guard let viewModel = viewModel else {
   6.158 -            Log.shared.errorAndCrash("View Model must not be nil")
   6.159 -            return
   6.160 -        }
   6.161 -        let alertController = UIAlertController.pEpAlertController(title: nil, message: nil,
   6.162 -                                                                   preferredStyle: .actionSheet)
   6.163 -        let enable = NSLocalizedString("Enable Protection", comment: "Enable Protection")
   6.164 -        let disable = NSLocalizedString("Disable Protection", comment: "Disable Protection")
   6.165 -        let toogleProtectionTitle = viewModel.pEpProtected  ? disable : enable
   6.166 -        let action = UIAlertAction(title: toogleProtectionTitle, style: .default) { (action) in
   6.167 -            viewModel.handleToggleProtectionPressed()
   6.168 -        }
   6.169 -        alertController.addAction(action)
   6.170 -        let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
   6.171 -        let cancelAction = UIAlertAction(title:cancelTitle , style: .cancel) { _ in
   6.172 -            alertController.dismiss(animated: true, completion: nil)
   6.173 -        }
   6.174 -        alertController.addAction(cancelAction)
   6.175 -        
   6.176 -        /// A broken contraint comes up, it's a known issue in iOS.
   6.177 -        /// https://github.com/lionheart/openradar-mirror/issues/21120
   6.178 -        if let buttonView = optionsButton.value(forKey: "view") as? UIView {
   6.179 -            alertController.popoverPresentationController?.sourceView = buttonView
   6.180 -            alertController.popoverPresentationController?.sourceRect = buttonView.bounds
   6.181 -        }
   6.182 -        present(alertController, animated: true, completion: nil)
   6.183 -    }
   6.184 -    
   6.185 -    /// Shows an action sheet with languages when the user taps the language button from a cell
   6.186 -    /// - Parameter cell: The cell of the language button tapped.
   6.187 -    private func showLanguagesList(for cell: TrustManagementTableViewCell) {
   6.188 -        guard let indexPath = tableView.indexPath(for: cell) else {
   6.189 -            Log.shared.error("IndexPath not found")
   6.190 -            return
   6.191 -        }
   6.192 -        let alertController = UIAlertController.pEpAlertController(title: nil,
   6.193 -                                                                   message: nil,
   6.194 -                                                                   preferredStyle: .actionSheet)
   6.195 -        guard let languages = viewModel?.handleChangeLanguagePressed(forRowAt: indexPath) else {
   6.196 -            Log.shared.error("Languages not found")
   6.197 -            return
   6.198 -        }
   6.199 -        //For every language a row in the action sheet.
   6.200 -        for language in languages {
   6.201 -            guard let languageName = NSLocale.current.localizedString(forLanguageCode: language)
   6.202 -                else {
   6.203 -                    Log.shared.debug("Language name not found")
   6.204 -                    break
   6.205 -            }
   6.206 -            let action = UIAlertAction(title: languageName, style: .default) { [weak self] (action) in
   6.207 -                guard let me = self else {
   6.208 -                    Log.shared.error("Lost myself")
   6.209 -                    return
   6.210 -                }
   6.211 -                me.viewModel?.didSelectLanguage(forRowAt: indexPath,
   6.212 -                                                language: language)
   6.213 -            }
   6.214 -            alertController.addAction(action)
   6.215 -        }
   6.216 -        
   6.217 -        //For the cancel button another action.
   6.218 -        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in
   6.219 -            alertController.dismiss(animated: true, completion: nil)
   6.220 -        }
   6.221 -        alertController.addAction(cancelAction)
   6.222 -
   6.223 -        //Ipad behavior.
   6.224 -        alertController.popoverPresentationController?.sourceView = cell.languageButton
   6.225 -        alertController.popoverPresentationController?.sourceRect = cell.languageButton.bounds
   6.226 -        present(alertController, animated: true, completion: nil)
   6.227 -    }
   6.228 -}
   6.229 -
   6.230 -/// MARK: - Handshake ViewModel Delegate
   6.231 -
   6.232 -extension TrustManagementViewController: TrustManagementViewModelDelegate {
   6.233 -    
   6.234 -    @objc public func reload() {
   6.235 -        UIView.setAnimationsEnabled(false)
   6.236 -        tableView.reloadData()
   6.237 -        UIView.setAnimationsEnabled(true)
   6.238 -    }
   6.239 -}
   6.240 -
   6.241 -/// MARK: - Back button
   6.242 -
   6.243 -extension TrustManagementViewController {
   6.244 -    
   6.245 -    /// Helper method to create and set the back button in the navigation bar.
   6.246 -    private func setLeftBarButton() {
   6.247 -        let title = NSLocalizedString(" Message",
   6.248 -                                      comment: "TrustManagementView Back Button Title")
   6.249 -        let button = UIButton.backButton(with: title)
   6.250 -        let action = #selector(backButtonPressed)
   6.251 -        button.addTarget(self, action:action, for: .touchUpInside)
   6.252 -        let leftItem = UIBarButtonItem(customView: button)
   6.253 -        navigationItem.leftBarButtonItem = leftItem
   6.254 -        navigationController?.navigationBar.isTranslucent = false
   6.255 -    }
   6.256 -
   6.257 -    @objc private func backButtonPressed() {
   6.258 -        dismiss(animated: true, completion: nil)
   6.259 -    }
   6.260 -}
   6.261 -
   6.262 -/// MARK: - Notification Center
   6.263 -
   6.264 -extension TrustManagementViewController {
   6.265 -    
   6.266 -    private func registerForNotifications() {
   6.267 -        NotificationCenter.default.addObserver(self, selector: #selector(reload),
   6.268 -                                               name: UIDevice.orientationDidChangeNotification,
   6.269 -                                               object: nil)
   6.270 -    }
   6.271 -    
   6.272 -    private func unregisterNotifications() {
   6.273 -        NotificationCenter.default.removeObserver(self)
   6.274 -    }
   6.275 -}
   6.276 -
   6.277 -/// MARK: - Set trustwords
   6.278 -
   6.279 -extension TrustManagementViewController {
   6.280 -    
   6.281 -    /// Generates and sets the trustwords to the cell
   6.282 -    /// - Parameters:
   6.283 -    ///   - cell: The cell where the trustwords would be setted
   6.284 -    ///   - indexPath: The indexPath of the row to generate the trustwords.
   6.285 -    ///   - longMode: Indicates if the trustwords have to be long.
   6.286 -    private func setTrustwords(for cell: TrustManagementTableViewCell,
   6.287 -                               at indexPath: IndexPath,
   6.288 -                               longMode: Bool) {
   6.289 -        guard let vm = viewModel else {
   6.290 -            Log.shared.errorAndCrash("No VM")
   6.291 -            return
   6.292 -        }
   6.293 -        vm.generateTrustwords(forRowAt: indexPath, long: longMode) { [weak self] trustwords in
   6.294 -            guard let trustwords = trustwords else {
   6.295 -                Log.shared.debug("Trustwords are nil. The view must not be updated")
   6.296 -                return
   6.297 -            }
   6.298 -            guard let me = self else {
   6.299 -                Log.shared.error("Lost myself")
   6.300 -                return
   6.301 -            }
   6.302 -
   6.303 -            let oneSpace = " "
   6.304 -            let threeSpaces = "   "
   6.305 -            let spacedTrustwords = trustwords.replacingOccurrences(of: oneSpace, with: threeSpaces)
   6.306 -            let textToSet = longMode ? spacedTrustwords : "\(spacedTrustwords)…"
   6.307 -            if (cell.trustwordsLabel.text != textToSet) {
   6.308 -                cell.trustwordsLabel.text = textToSet
   6.309 -                me.tableView.updateSize()
   6.310 -            }
   6.311 -        }
   6.312 -    }
   6.313 -}
   6.314 -
   6.315 -/// MARK: - TrustManagementTableViewCellDelegate
   6.316 -
   6.317 -extension TrustManagementViewController: TrustManagementTableViewCellDelegate,
   6.318 -TrustManagementResetTableViewCellDelegate {
   6.319 -    func languageButtonPressed(on cell: TrustManagementTableViewCell) {
   6.320 -        showLanguagesList(for: cell)
   6.321 -    }
   6.322 -    
   6.323 -    func declineButtonPressed(on cell: TrustManagementTableViewCell) {
   6.324 -        guard let viewModel = viewModel else {
   6.325 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   6.326 -            return
   6.327 -        }
   6.328 -        if let indexPath = tableView.indexPath(for: cell) {
   6.329 -            viewModel.handleRejectHandshakePressed(at: indexPath)
   6.330 -        }
   6.331 -    }
   6.332 -    
   6.333 -    func confirmButtonPressed(on cell: TrustManagementTableViewCell) {
   6.334 -        guard let viewModel = viewModel else {
   6.335 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   6.336 -            return
   6.337 -        }
   6.338 -
   6.339 -        if let indexPath = tableView.indexPath(for: cell) {
   6.340 -            viewModel.handleConfirmHandshakePressed(at: indexPath)
   6.341 -        }
   6.342 -    }
   6.343 -    
   6.344 -    func resetButtonPressed(on cell: UITableViewCell) {
   6.345 -        guard let viewModel = viewModel else {
   6.346 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   6.347 -            return
   6.348 -        }
   6.349 -        if let indexPath = tableView.indexPath(for: cell) {
   6.350 -            viewModel.handleResetPressed(forRowAt: indexPath)
   6.351 -        }
   6.352 -    }
   6.353 -    
   6.354 -    func trustwordsLabelPressed(on cell: TrustManagementTableViewCell) {
   6.355 -        guard let viewModel = viewModel else {
   6.356 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   6.357 -            return
   6.358 -        }
   6.359 -        if let indexPath = tableView.indexPath(for: cell) {
   6.360 -            viewModel.handleToggleLongTrustwords(forRowAt: indexPath)
   6.361 -        }
   6.362 -    }
   6.363 -}
   6.364 -
   6.365 -/// MARK: - Cell configuration
   6.366 -
   6.367 -extension TrustManagementViewController {
   6.368 -    
   6.369 -    /// This method configures the layout for the provided cell.
   6.370 -    /// We use 2 different cells: one for the split view the other for iphone portrait view.
   6.371 -    /// The layout is different, so different UI structures are used.
   6.372 -    /// 
   6.373 -    /// - Parameters:
   6.374 -    ///   - identifier: As we handle two different cells, the identifier is required in order to set the layout properly.
   6.375 -    ///   - row: The row to get information to configure the cell.
   6.376 -    ///   - cell: The cell to be configured.
   6.377 -    ///   - indexPath: The indexPath of the row, to get the trustwords.
   6.378 -    private func configureTrustwords(_ identifier: String, _ row: TrustManagementViewModel.Row, _ cell: TrustManagementTableViewCell, _ indexPath: IndexPath) {
   6.379 -        ///Yellow means secure but not trusted.
   6.380 -        ///That means that's the only case must display the trustwords
   6.381 -        if identifier == onlyMasterCellIdentifier {
   6.382 -            if row.color == .yellow {
   6.383 -                setTrustwords(for: cell, at: indexPath, longMode: row.longTrustwords)
   6.384 -                cell.trustwordsStackView.isHidden = false
   6.385 -                cell.trustwordsButtonsContainer.isHidden = false
   6.386 -            } else {
   6.387 -                cell.trustwordsStackView.isHidden = true
   6.388 -                cell.trustwordsButtonsContainer.isHidden = true
   6.389 -            }
   6.390 -        } else if identifier == masterAndDetailCellIdentifier {
   6.391 -            if row.color == .yellow {
   6.392 -                setTrustwords(for: cell, at: indexPath, longMode: row.longTrustwords)
   6.393 -                cell.trustwordsLabel.isHidden = false
   6.394 -                cell.confirmButton.isHidden = false
   6.395 -                cell.declineButton.isHidden = false
   6.396 -                cell.languageButton.isHidden = false
   6.397 -            } else {
   6.398 -                cell.languageButton.isHidden = true
   6.399 -                cell.trustwordsLabel.isHidden = true
   6.400 -                cell.confirmButton.isHidden = true
   6.401 -                cell.declineButton.isHidden = true
   6.402 -            }
   6.403 -        }
   6.404 -    }
   6.405 -}
     7.1 --- a/pEpForiOS/UI/Handshake/TrustManagementViewModel.swift	Wed Mar 11 19:08:34 2020 +0100
     7.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.3 @@ -1,382 +0,0 @@
     7.4 -//
     7.5 -//  TrustManagementViewModel.swift
     7.6 -//  pEp
     7.7 -//
     7.8 -//  Created by Martin Brude on 30/01/2020.
     7.9 -//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    7.10 -//
    7.11 -
    7.12 -import Foundation
    7.13 -import MessageModel
    7.14 -import PEPObjCAdapterFramework
    7.15 -
    7.16 -/// TrustManagementViewModel View Mode Delegate
    7.17 -protocol TrustManagementViewModelDelegate: class {
    7.18 -    /// Delegate method to notify that an action ends and the view must be reloaded.
    7.19 -    func reload()
    7.20 -}
    7.21 -
    7.22 -protocol TrustmanagementProtectionStateChangeDelegate: class {
    7.23 -    /// Called whenever the user toggles protection state (for the message)
    7.24 -    func protectionStateChanged(to newValue: Bool)
    7.25 -}
    7.26 -
    7.27 -extension TrustManagementViewModel {
    7.28 -    /// The item that represents the handshake partner
    7.29 -    public struct Row {
    7.30 -        /// Indicates the handshake partner's name
    7.31 -        var name: String {
    7.32 -            let name = handshakeCombination.partnerIdentity.userName
    7.33 -            let address = handshakeCombination.partnerIdentity.address
    7.34 -            return name ?? address
    7.35 -        }
    7.36 -        /// The description for the row
    7.37 -        var description: String {
    7.38 -            if forceRed {
    7.39 -                return PEPColor.red.privacyStatusDescription
    7.40 -            }
    7.41 -            return color.privacyStatusDescription
    7.42 -        }
    7.43 -        /// The privacy status name
    7.44 -        var privacyStatusName: String {
    7.45 -            if (forceRed) {
    7.46 -                return String.trustIdentityTranslation(pEpRating: .underAttack).title
    7.47 -            }
    7.48 -            let rating = handshakeCombination.partnerIdentity.pEpRating()
    7.49 -            let translations = String.trustIdentityTranslation(pEpRating: rating)
    7.50 -            return translations.title
    7.51 -        }
    7.52 -        /// The privacy status image
    7.53 -        var privacyStatusImage: UIImage? {
    7.54 -            if forceRed {
    7.55 -                return PEPColor.red.statusIconForMessage(enabled: true, withText: false)
    7.56 -            }
    7.57 -            return color.statusIconForMessage(enabled: true, withText: false)
    7.58 -        }
    7.59 -        /// The current language
    7.60 -        var currentLanguage: String
    7.61 -        /// Indicates if the trustwords are long
    7.62 -        var longTrustwords: Bool = false
    7.63 -        /// The privacy status in between the current user and the partner
    7.64 -        var privacyStatus: String?
    7.65 -        /// Status indicator
    7.66 -        var color : PEPColor {
    7.67 -            if forceRed {
    7.68 -                return PEPColor.red
    7.69 -            }
    7.70 -            return handshakeCombination.partnerIdentity.pEpColor()
    7.71 -        }
    7.72 -        var trustwords : String?
    7.73 -        //Prevents the overkill of require the trustwords when it's not necesary.
    7.74 -        fileprivate var shouldUpdateTrustwords : Bool = true
    7.75 -        fileprivate var forceRed: Bool = false
    7.76 -        /// The identity of the user to do the handshake
    7.77 -        fileprivate var handshakeCombination: TrustManagementUtil.HandshakeCombination
    7.78 -        fileprivate var fingerprint: String?
    7.79 -    }
    7.80 -}
    7.81 -
    7.82 -/// View Model to handle the TrustManagementViewModel views.
    7.83 -final class TrustManagementViewModel {
    7.84 -    weak public var delegate : TrustManagementViewModelDelegate?
    7.85 -    weak public var protectionStateChangeDelegate: TrustmanagementProtectionStateChangeDelegate?
    7.86 -    public var pEpProtected : Bool {
    7.87 -        didSet {
    7.88 -            protectionStateChangeDelegate?.protectionStateChanged(to: pEpProtected)
    7.89 -        }
    7.90 -    }
    7.91 -    var shouldShowOptionsButton: Bool = false
    7.92 -    private var message: Message
    7.93 -    private var trustManagementUtil : TrustManagementUtilProtocol?
    7.94 -    private let undoManager = UndoManager()
    7.95 -    private var actionPerformed = [String]()
    7.96 -    
    7.97 -    /// Items to be displayed in the View Controller
    7.98 -    private (set) var rows: [Row] = [Row]()
    7.99 -
   7.100 -    /// Constructor
   7.101 -    /// - Parameters:
   7.102 -    ///   - message: The message to manage the trust
   7.103 -    ///   - handshakeUtil: The tool to interact with the engine. It provides a default instance. The parameter is used for testing purposes.
   7.104 -    public init(message : Message,
   7.105 -                pEpProtectionModifyable: Bool,
   7.106 -                delegate : TrustManagementViewModelDelegate? = nil,
   7.107 -                protectionStateChangeDelegate: TrustmanagementProtectionStateChangeDelegate? = nil,
   7.108 -                trustManagementUtil: TrustManagementUtilProtocol? = TrustManagementUtil()) {
   7.109 -        self.message = message
   7.110 -        self.trustManagementUtil = trustManagementUtil
   7.111 -        self.pEpProtected = message.pEpProtected
   7.112 -        self.shouldShowOptionsButton = pEpProtectionModifyable
   7.113 -        self.delegate = delegate
   7.114 -        self.protectionStateChangeDelegate = protectionStateChangeDelegate
   7.115 -        generateRows()
   7.116 -    }
   7.117 -
   7.118 -    ///MARK - Actions
   7.119 -    
   7.120 -    /// Reject the handshake
   7.121 -    /// - Parameter indexPath: The indexPath of the item to get the user to reject the handshake
   7.122 -    public func handleRejectHandshakePressed(at indexPath: IndexPath) {
   7.123 -        guard let trustManagementViewModel = trustManagementUtil else {
   7.124 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   7.125 -            return
   7.126 -        }
   7.127 -        let actionName = NSLocalizedString("Trust Rejection", comment: "Action name to be suggested at the moment of revert")
   7.128 -        actionPerformed.append(actionName)
   7.129 -        registerUndoAction(at: indexPath)
   7.130 -        let row = rows[indexPath.row]
   7.131 -        let identity : Identity = row.handshakeCombination.partnerIdentity.safeForSession(Session.main)
   7.132 -        rows[indexPath.row].fingerprint = trustManagementViewModel.getFingerprint(for: identity)
   7.133 -        rows[indexPath.row].forceRed = true
   7.134 -        trustManagementViewModel.denyTrust(for: identity)
   7.135 -        reevaluateMessage()
   7.136 -        delegate?.reload()
   7.137 -    }
   7.138 -    
   7.139 -    /// Confirm the handshake
   7.140 -    /// - Parameter indexPath: The indexPath of the item to get the user to confirm the handshake
   7.141 -    public func handleConfirmHandshakePressed(at indexPath: IndexPath) {
   7.142 -        guard let trustManagementViewModel = trustManagementUtil else {
   7.143 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   7.144 -            return
   7.145 -        }
   7.146 -        let actionName = NSLocalizedString("Trust Confirmation", comment: "Action name to be suggested at the moment of revert")
   7.147 -        actionPerformed.append(actionName)
   7.148 -        registerUndoAction(at: indexPath)
   7.149 -        let row = rows[indexPath.row]
   7.150 -        rows[indexPath.row].forceRed = false
   7.151 -        let identity : Identity = row.handshakeCombination.partnerIdentity.safeForSession(Session.main)
   7.152 -        trustManagementViewModel.confirmTrust(for: identity)
   7.153 -        reevaluateMessage()
   7.154 -        delegate?.reload()
   7.155 -    }
   7.156 -    
   7.157 -    /// Handles the undo action.
   7.158 -    /// That means that the trust will be reseted.
   7.159 -    /// So it is not important what action in concrete was performed.
   7.160 -    /// - Parameter indexPath: The index path of the row from where the last action has been performed.
   7.161 -    @objc public func handleUndo(forRowAt indexPath: IndexPath) {
   7.162 -        guard let trustManagementViewModel = trustManagementUtil else {
   7.163 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   7.164 -            return
   7.165 -        }
   7.166 -        let row = rows[indexPath.row]
   7.167 -        rows[indexPath.row].shouldUpdateTrustwords = true
   7.168 -        rows[indexPath.row].forceRed = false
   7.169 -        trustManagementViewModel.undoMisstrustOrTrust(for: row.handshakeCombination.partnerIdentity,
   7.170 -                                                      fingerprint: row.fingerprint)
   7.171 -        reevaluateMessage()
   7.172 -    }
   7.173 -    
   7.174 -    /// Handles the redey action
   7.175 -    /// - Parameter indexPath: The indexPath of the item to get the user to undo last action.
   7.176 -    public func handleResetPressed(forRowAt indexPath: IndexPath) {
   7.177 -        guard let trustManagementViewModel = trustManagementUtil else {
   7.178 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   7.179 -            return
   7.180 -        }
   7.181 -        let row = rows[indexPath.row]
   7.182 -        rows[indexPath.row].forceRed = false
   7.183 -        trustManagementViewModel.resetTrust(for: row.handshakeCombination.partnerIdentity)
   7.184 -        reevaluateMessage()
   7.185 -        delegate?.reload()
   7.186 -    }
   7.187 -
   7.188 -    /// - returns: the available languages.
   7.189 -    public func handleChangeLanguagePressed(forRowAt indexPath : IndexPath) -> [String] {
   7.190 -        guard let trustManagementViewModel = trustManagementUtil else {
   7.191 -            Log.shared.errorAndCrash("TrustManagementViewModelDelegate is nil")
   7.192 -            return [String]()
   7.193 -        }
   7.194 -        rows[indexPath.row].shouldUpdateTrustwords = true
   7.195 -        guard let list = trustManagementViewModel.languagesList() else {
   7.196 -            Log.shared.error("The list of languages could be retrieved.")
   7.197 -            return [String]()
   7.198 -        }
   7.199 -        return list
   7.200 -    }
   7.201 -    
   7.202 -    /// Updates the selected language for that row.
   7.203 -    /// - Parameters:
   7.204 -    ///   - indexPath: The index path of the row
   7.205 -    ///   - language: The chosen language
   7.206 -    public func didSelectLanguage(forRowAt indexPath: IndexPath, language: String) {
   7.207 -        guard let trustManagementViewModelDelegate = delegate else {
   7.208 -            Log.shared.errorAndCrash("TrustManagementViewModelDelegate is nil")
   7.209 -            return
   7.210 -        }
   7.211 -        rows[indexPath.row].currentLanguage = language
   7.212 -        trustManagementViewModelDelegate.reload()
   7.213 -    }
   7.214 -    
   7.215 -    /// Toogle pEp protection status
   7.216 -    public func handleToggleProtectionPressed() {
   7.217 -        pEpProtected = !pEpProtected
   7.218 -    }
   7.219 -
   7.220 -    /// Informs if is it possible to undo an action.
   7.221 -    /// - returns: Indicates if it's possible to undo an action.
   7.222 -    public func canUndo() -> Bool {
   7.223 -        return undoManager.canUndo
   7.224 -    }
   7.225 -    
   7.226 -    /// - returns: The name of the last action performed, nil if there isn't any.
   7.227 -    public func lastActionPerformed() -> String? {
   7.228 -        return actionPerformed.last
   7.229 -    }
   7.230 -
   7.231 -    /// Method that makes the trustwords long or short (more or less trustwords in fact).
   7.232 -    /// - Parameter indexPath: The indexPath to get the row to toogle the status (long/short)
   7.233 -    public func handleToggleLongTrustwords(forRowAt indexPath: IndexPath) {
   7.234 -        guard let trustManagementViewModelDelegate = delegate else {
   7.235 -            Log.shared.errorAndCrash("TrustManagementViewModelDelegate is nil")
   7.236 -            return
   7.237 -        }
   7.238 -        rows[indexPath.row].shouldUpdateTrustwords = true
   7.239 -        rows[indexPath.row].longTrustwords.toggle()
   7.240 -        trustManagementViewModelDelegate.reload()
   7.241 -    }
   7.242 -
   7.243 -    public typealias TrustWords = String
   7.244 -
   7.245 -    /// Generate the trustwords
   7.246 -    /// - Parameters:
   7.247 -    ///   - indexPath: The indexPath of the row to toogle the status (long/short)
   7.248 -    ///   - long: Indicates if the trustwords MUST be long (more words)
   7.249 -    ///   - completion: the completion block to be executed once the trustwords are generated.
   7.250 -    /// If the trustwords passed are nil the UI must no be updated.
   7.251 -    /// If the completion block is nil, only the model in the row at the provided indexPath will be updated.
   7.252 -    public func generateTrustwords(forRowAt indexPath: IndexPath,
   7.253 -                                   long : Bool = false,
   7.254 -                                   completion: ((TrustWords?) -> Void)?)  {
   7.255 -        guard rows[indexPath.row].shouldUpdateTrustwords else {
   7.256 -            if let trustwords = rows[indexPath.row].trustwords {
   7.257 -                completion?(trustwords)
   7.258 -                return
   7.259 -            }
   7.260 -            completion?(nil)
   7.261 -            return
   7.262 -        }
   7.263 -        rows[indexPath.row].shouldUpdateTrustwords = false
   7.264 -        DispatchQueue.global(qos: .userInteractive).async { [weak self] in
   7.265 -            guard let me = self,
   7.266 -                let trustManagementViewModel = me.trustManagementUtil else {
   7.267 -                completion?(nil)
   7.268 -                Log.shared.errorAndCrash("Lost myself or the trustManagement ViewModel")
   7.269 -                return
   7.270 -            }
   7.271 -            let complete: (TrustWords?) -> Void = { trustwords in
   7.272 -                DispatchQueue.main.async {
   7.273 -                    completion?(trustwords)
   7.274 -                }
   7.275 -            }
   7.276 -            let handshakeItem = me.rows[indexPath.row]
   7.277 -            handshakeItem.handshakeCombination.ownIdentity.session.performAndWait {
   7.278 -                let selfIdentity = handshakeItem.handshakeCombination.ownIdentity
   7.279 -                let partnerIdentity = handshakeItem.handshakeCombination.partnerIdentity
   7.280 -                guard let trustwords = try? trustManagementViewModel.getTrustwords(for: selfIdentity,
   7.281 -                                                                                   and: partnerIdentity,
   7.282 -                                                                                   language: handshakeItem.currentLanguage,
   7.283 -                                                                                   long: long)
   7.284 -                    else {
   7.285 -                        Log.shared.errorAndCrash("No Trustwords")
   7.286 -                        completion?(nil)
   7.287 -                        return
   7.288 -                }
   7.289 -                me.rows[indexPath.row].trustwords = trustwords
   7.290 -                complete(trustwords)
   7.291 -            }
   7.292 -        }
   7.293 -    }
   7.294 -
   7.295 -    /// Method that reverts the last action performed by the user
   7.296 -    /// After the execution of this method there won't be any action to un-do.
   7.297 -    public func shakeMotionDidEnd() {
   7.298 -       /// Evaluate it the undo manager can undo means if it has something registerd to undo.
   7.299 -        /// If so, undo it and reload the view.
   7.300 -        if (undoManager.canUndo) {
   7.301 -            undoManager.undo()
   7.302 -            delegate?.reload()
   7.303 -            _ = actionPerformed.popLast()
   7.304 -        }
   7.305 -    }
   7.306 -
   7.307 -    ///MARK: - Private
   7.308 -
   7.309 -    /// This must be called after every trust state change. The curently processed message might
   7.310 -    /// change color.
   7.311 -    private func reevaluateMessage() {
   7.312 -        message.session.performAndWait { [weak self] in
   7.313 -            guard let me = self else {
   7.314 -                Log.shared.error("Lost myself - The message will not be reevaluated")
   7.315 -                return
   7.316 -            }
   7.317 -            RatingReEvaluator.reevaluate(message: me.message)
   7.318 -        }
   7.319 -    }
   7.320 -
   7.321 -    /// Method that generates the rows to be used by the VC
   7.322 -    private func generateRows() {
   7.323 -        guard let trustManagementViewModel = trustManagementUtil else {
   7.324 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   7.325 -            return
   7.326 -        }
   7.327 -        let combinations = trustManagementViewModel.handshakeCombinations(message: message)
   7.328 -
   7.329 -        for (index, combination) in combinations.enumerated() {
   7.330 -            let backupLanguage = "en"
   7.331 -            let language =
   7.332 -            combination.partnerIdentity.language ?? Locale.current.languageCode ?? backupLanguage
   7.333 -            let row = Row(currentLanguage: language,
   7.334 -                          longTrustwords: false,
   7.335 -                          handshakeCombination: combination)
   7.336 -            rows.append(row)
   7.337 -            let indexPath = IndexPath(row: index, section: 0)
   7.338 -            generateTrustwords(forRowAt: indexPath, completion: nil)
   7.339 -        }
   7.340 -    }
   7.341 -
   7.342 -    /// Register the action to be undone
   7.343 -    /// Is not important determine what action in concret must be undone.
   7.344 -    /// The trust management util -and thus, the engine- will take care of that.
   7.345 -    /// - Parameter indexPath: The indexPath of the row which the action to undo.
   7.346 -    private func registerUndoAction(at indexPath: IndexPath) {
   7.347 -        undoManager.registerUndo(withTarget: self,
   7.348 -                                 selector: #selector(handleUndo(forRowAt:)),
   7.349 -                                 object: indexPath)
   7.350 -    }
   7.351 -}
   7.352 -
   7.353 -/// MARK: - Image 
   7.354 -
   7.355 -extension TrustManagementViewModel {
   7.356 -    
   7.357 -    /// Method that returns the user image for the current indexPath throught the callback
   7.358 -    /// - Parameters:
   7.359 -    ///   - indexPath: The index path to get the user image
   7.360 -    ///   - complete: The callback with the image
   7.361 -    public func getImage(forRowAt indexPath: IndexPath, complete: @escaping (UIImage?) -> ()) {
   7.362 -        
   7.363 -        //Check if it's cached, use it if so.
   7.364 -        let handshakeItem : Row = rows[indexPath.row]
   7.365 -        let partnerIdentity = handshakeItem.handshakeCombination.partnerIdentity
   7.366 -        let contactImageTool = IdentityImageTool()
   7.367 -        let key = IdentityImageTool.IdentityKey(identity: partnerIdentity)
   7.368 -        if let cachedContactImage = contactImageTool.cachedIdentityImage(for: key) {
   7.369 -            complete(cachedContactImage)
   7.370 -            return
   7.371 -        }
   7.372 -        
   7.373 -        //If can't find the image in the cache, creates it from the session.
   7.374 -        let session = Session()
   7.375 -        let safePartnerIdentity = partnerIdentity.safeForSession(session)
   7.376 -        DispatchQueue.global(qos: .userInteractive).async {
   7.377 -            session.performAndWait {
   7.378 -                if let contactImage = contactImageTool.identityImage(for:
   7.379 -                    IdentityImageTool.IdentityKey(identity: safePartnerIdentity)) {
   7.380 -                    complete(contactImage)
   7.381 -                }
   7.382 -            }
   7.383 -        }
   7.384 -    }
   7.385 -}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/pEpForiOS/UI/TrustManagement/CellsAndSections/TrustManagementResetTableViewCell.swift	Wed Mar 11 19:09:12 2020 +0100
     8.3 @@ -0,0 +1,35 @@
     8.4 +//
     8.5 +//  TrustManagementResetTableViewCell.swift
     8.6 +//  pEp
     8.7 +//
     8.8 +//  Created by Martin Brude on 17/02/2020.
     8.9 +//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    8.10 +//
    8.11 +
    8.12 +import UIKit
    8.13 +
    8.14 +final class TrustManagementResetTableViewCell: UITableViewCell {
    8.15 +
    8.16 +    @IBOutlet weak var partnerNameLabel: UILabel!
    8.17 +    @IBOutlet weak var partnerImageView: UIImageView!
    8.18 +    @IBOutlet weak var resetLabel: UILabel!
    8.19 +    @IBOutlet weak var resetButton: UIButton!
    8.20 +    
    8.21 +    weak var delegate : TrustManagementResetTableViewCellDelegate?
    8.22 +
    8.23 +    override func awakeFromNib() {
    8.24 +        super.awakeFromNib()
    8.25 +        
    8.26 +           //Reset Button
    8.27 +           resetButton.pEpIfyForTrust(backgroundColor: .pEpGrayBackgroundReset, textColor: .white)
    8.28 +        
    8.29 +           //Reset label
    8.30 +           resetLabel.text = NSLocalizedString("Reset all p≡p data for this comunication partner:",
    8.31 +                                               comment: "Reset all p≡p data for this comunication partner:")
    8.32 +
    8.33 +    }
    8.34 +
    8.35 +    @IBAction private func resetButtonPressed() {
    8.36 +        delegate?.resetButtonPressed(on: self)
    8.37 +    }
    8.38 +}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/pEpForiOS/UI/TrustManagement/CellsAndSections/TrustManagementTableViewCell.swift	Wed Mar 11 19:09:12 2020 +0100
     9.3 @@ -0,0 +1,104 @@
     9.4 +//
     9.5 +//  HandshakePartnerTableViewCell.swift
     9.6 +//  pEp
     9.7 +//
     9.8 +//  Created by Martin Brude on 07/02/2020.
     9.9 +//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    9.10 +//
    9.11 +
    9.12 +import UIKit
    9.13 +
    9.14 +/// UITableViewCell for trust management screen
    9.15 +final class TrustManagementTableViewCell: UITableViewCell {
    9.16 + 
    9.17 +    //Content
    9.18 +    @IBOutlet weak var partnerImageView: UIImageView!
    9.19 +    @IBOutlet weak var privacyStatusImageView: UIImageView!
    9.20 +    @IBOutlet weak var partnerNameLabel: UILabel!
    9.21 +    @IBOutlet weak var privacyStatusLabel: UILabel!
    9.22 +    @IBOutlet weak var trustwordsLabel: UILabel!
    9.23 +    @IBOutlet weak var descriptionLabel: UILabel!
    9.24 +    @IBOutlet weak var languageButton: UIButton!
    9.25 +    
    9.26 +    //Only for i18n and layout
    9.27 +    @IBOutlet weak var resetLabel: UILabel!
    9.28 +    @IBOutlet weak var declineButton: UIButton!
    9.29 +    @IBOutlet weak var confirmButton: UIButton!
    9.30 +    @IBOutlet weak var resetButton: UIButton!
    9.31 +    
    9.32 +    //Hide these views in case pepColor is not yellow.
    9.33 +    @IBOutlet weak var trustwordsStackView: UIStackView!
    9.34 +    @IBOutlet weak var trustwordsButtonsContainer: UIView!
    9.35 +    
    9.36 +    weak var delegate : TrustManagementTableViewCellDelegate?
    9.37 +    
    9.38 +    override func awakeFromNib() {
    9.39 +        super.awakeFromNib()
    9.40 +        setupView()
    9.41 +    }
    9.42 +    
    9.43 +    /// Reset attributes of the cell that are not related to content.
    9.44 +    override func prepareForReuse() {
    9.45 +        super.prepareForReuse()
    9.46 +        removeGestureRecognizers()
    9.47 +    }
    9.48 +
    9.49 +    // MARK: - Actions
    9.50 +    
    9.51 +    @IBAction private func languageButtonPressed() {
    9.52 +        delegate?.languageButtonPressed(on: self)
    9.53 +    }
    9.54 +    
    9.55 +    @IBAction private func declineButtonPressed() {
    9.56 +        delegate?.declineButtonPressed(on: self)
    9.57 +    }
    9.58 +
    9.59 +    @IBAction private func confirmButtonPressed() {
    9.60 +        delegate?.confirmButtonPressed(on: self)
    9.61 +    }
    9.62 +
    9.63 +    @IBAction private func resetButtonPressed() {
    9.64 +        delegate?.resetButtonPressed(on: self)
    9.65 +    }
    9.66 +    
    9.67 +    @objc private func trustwordsLabelPressed() {
    9.68 +        delegate?.trustwordsLabelPressed(on: self)
    9.69 +    }
    9.70 +    
    9.71 +    // MARK: - Private
    9.72 +    
    9.73 +    /// Setup the view with the row data.
    9.74 +    private func setupView() {
    9.75 +        removeGestureRecognizers()
    9.76 +
    9.77 +        let gesture = UITapGestureRecognizer(target: self, action: #selector(trustwordsLabelPressed))
    9.78 +        trustwordsLabel.addGestureRecognizer(gesture)
    9.79 +    
    9.80 +        //Confirm Button
    9.81 +        let confirmTitle = NSLocalizedString("Confirm", comment: "Confirm correct trustwords/PGP fingerprint")
    9.82 +        confirmButton.setTitle(confirmTitle, for: .normal)
    9.83 +        confirmButton.pEpIfyForTrust(backgroundColor: .pEpGreen, textColor: .white)
    9.84 +        
    9.85 +        //Decline Button
    9.86 +        let declineTitle = NSLocalizedString("Decline", comment: "Incorrect trustwords/PGP fingerprint")
    9.87 +        declineButton.setTitle(declineTitle, for: .normal)
    9.88 +        declineButton.pEpIfyForTrust(backgroundColor: .pEpRed, textColor: .white)
    9.89 +
    9.90 +        //Reset Button
    9.91 +        resetButton.pEpIfyForTrust(backgroundColor: .pEpGrayBackgroundReset, textColor: .white)
    9.92 +     
    9.93 +        //Reset label
    9.94 +        resetLabel.text = NSLocalizedString("Reset all p≡p data for this comunication partner:",
    9.95 +                                            comment: "Reset all p≡p data for this comunication partner:")
    9.96 +        //Image view
    9.97 +        partnerImageView.layer.cornerRadius = 10
    9.98 +        partnerImageView.layer.masksToBounds = true
    9.99 +    }
   9.100 +
   9.101 +    private func removeGestureRecognizers() {
   9.102 +        let existingGRs = gestureRecognizers ?? []
   9.103 +        for gr in existingGRs {
   9.104 +            removeGestureRecognizer(gr)
   9.105 +        }
   9.106 +    }
   9.107 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/pEpForiOS/UI/TrustManagement/CellsAndSections/TrustManagementTableViewCellProtocols.swift	Wed Mar 11 19:09:12 2020 +0100
    10.3 @@ -0,0 +1,33 @@
    10.4 +//
    10.5 +//  TrustManagementTableViewCellProtocols.swift
    10.6 +//  pEp
    10.7 +//
    10.8 +//  Created by Martin Brude on 18/02/2020.
    10.9 +//  Copyright © 2020 p≡p Security S.A. All rights reserved.
   10.10 +//
   10.11 +
   10.12 +import Foundation
   10.13 +
   10.14 +/// Delegate to notify the events in the cell.
   10.15 +protocol TrustManagementResetTableViewCellDelegate: class {
   10.16 +    /// Delegate method to notify the reset button has been pressed.
   10.17 +    /// - Parameter cell: The cell where the reset button has been pressed
   10.18 +    func resetButtonPressed(on cell: UITableViewCell)
   10.19 +}
   10.20 +
   10.21 +/// Delegate to notify the events in the cell.
   10.22 +protocol TrustManagementTableViewCellDelegate: TrustManagementResetTableViewCellDelegate {
   10.23 +    
   10.24 +    /// Delegate method to notify the language button has been pressed.
   10.25 +    /// - Parameter cell: The cell where the language button has been pressed
   10.26 +    func languageButtonPressed(on cell: TrustManagementTableViewCell)
   10.27 +    /// Delegate method to notify the decline button has been pressed.
   10.28 +    /// - Parameter cell: The cell where the decline button has been pressed
   10.29 +    func declineButtonPressed(on cell: TrustManagementTableViewCell)
   10.30 +    /// Delegate method to notify the confirm button has been pressed.
   10.31 +    /// - Parameter cell: The cell where the confirm button has been pressed
   10.32 +    func confirmButtonPressed(on cell: TrustManagementTableViewCell)
   10.33 +    /// Delegate method to notify the trustwords label has been pressed.
   10.34 +    /// - Parameter cell: The cell where the trustwords label has been pressed
   10.35 +    func trustwordsLabelPressed(on cell : TrustManagementTableViewCell)
   10.36 +}
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/pEpForiOS/UI/TrustManagement/TrustManagementViewController.swift	Wed Mar 11 19:09:12 2020 +0100
    11.3 @@ -0,0 +1,402 @@
    11.4 +//
    11.5 +//  TrustManagementViewController.swift
    11.6 +//  pEp
    11.7 +//
    11.8 +//  Created by Martin Brude on 05/02/2020.
    11.9 +//  Copyright © 2020 p≡p Security S.A. All rights reserved.
   11.10 +//
   11.11 +
   11.12 +import UIKit
   11.13 +
   11.14 +/// View Controller to handle the HandshakeView.
   11.15 +class TrustManagementViewController: BaseViewController {
   11.16 +    private let onlyMasterCellIdentifier = "TrustManagementTableViewCell_OnlyMaster"
   11.17 +    private let masterAndDetailCellIdentifier = "TrustManagementTableViewCell_Detailed"
   11.18 +    private let resetCellIdentifier = "TrustManagementTableViewResetCell"
   11.19 +    @IBOutlet weak var tableView: UITableView!
   11.20 +    @IBOutlet weak var optionsButton: UIBarButtonItem!
   11.21 +
   11.22 +    var viewModel : TrustManagementViewModel?
   11.23 +
   11.24 +    override func viewDidLoad() {
   11.25 +        super.viewDidLoad()
   11.26 +        setup()
   11.27 +        tableView.rowHeight = UITableView.automaticDimension
   11.28 +        tableView.estimatedRowHeight = 400
   11.29 +    }
   11.30 +
   11.31 +    override func viewWillAppear(_ animated: Bool) {
   11.32 +        super.viewWillAppear(animated)
   11.33 +        guard let vm = viewModel else {
   11.34 +            Log.shared.errorAndCrash("The viewModel must not be nil")
   11.35 +            return
   11.36 +        }
   11.37 +        if (!vm.shouldShowOptionsButton) {
   11.38 +            navigationItem.rightBarButtonItems?.removeAll(where: {$0 == optionsButton})
   11.39 +        } else {
   11.40 +            optionsButton.title = NSLocalizedString("Options", comment: "Options")
   11.41 +        }
   11.42 +        vm.delegate = self
   11.43 +    }
   11.44 +
   11.45 +    override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
   11.46 +        guard let vm = viewModel, vm.canUndo() && motion == .motionShake,
   11.47 +            let actionName = vm.lastActionPerformed() else { return }
   11.48 +        let title = NSLocalizedString("Undo \(actionName)", comment: "Undo trust change verification alert title")
   11.49 +        let alertController = UIAlertController.pEpAlertController(title: title,
   11.50 +                                                                   message: nil,
   11.51 +                                                                   preferredStyle: .alert)
   11.52 +        let confirmTitle = NSLocalizedString("Undo", comment: "Undo trust change verification button title")
   11.53 +        let action = UIAlertAction(title: confirmTitle, style: .default) { [weak vm] (action) in
   11.54 +            vm?.shakeMotionDidEnd()
   11.55 +        }
   11.56 +        alertController.addAction(action)
   11.57 +        
   11.58 +        //For the cancel button another action.
   11.59 +        let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel trust change to be undone")
   11.60 +        let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) { _ in
   11.61 +            alertController.dismiss(animated: true, completion: nil)
   11.62 +        }
   11.63 +        alertController.addAction(cancelAction)
   11.64 +        present(alertController, animated: true, completion: nil)
   11.65 +    }
   11.66 +
   11.67 +    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
   11.68 +        super.viewWillTransition(to: size, with: coordinator)
   11.69 +        tableView.reloadData()
   11.70 +    }
   11.71 +
   11.72 +    @IBAction private func optionsButtonPressed(_ sender: UIBarButtonItem) {
   11.73 +        presentToogleProtectionActionSheet()
   11.74 +    }
   11.75 +   
   11.76 +    deinit {
   11.77 +        unregisterNotifications()
   11.78 +    }
   11.79 +}
   11.80 +
   11.81 +// MARK: - Private
   11.82 +
   11.83 +extension TrustManagementViewController {
   11.84 +
   11.85 +    private func setup() {
   11.86 +        registerForNotifications()
   11.87 +        setLeftBarButton()
   11.88 +    }
   11.89 +}
   11.90 +
   11.91 +/// MARK: - UITableViewDataSource
   11.92 +
   11.93 +extension TrustManagementViewController : UITableViewDataSource  {
   11.94 +    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
   11.95 +        guard let numberOfRows = viewModel?.rows.count else {
   11.96 +            Log.shared.error("The viewModel must not be nil")
   11.97 +            return 0
   11.98 +        }
   11.99 +        return numberOfRows
  11.100 +    }
  11.101 +    
  11.102 +    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  11.103 +        guard let row = viewModel?.rows[indexPath.row] else {
  11.104 +            Log.shared.error("The row couldn't be dequeued")
  11.105 +            return UITableViewCell()
  11.106 +        }
  11.107 +        
  11.108 +        /// Cell for reset
  11.109 +        if row.color == .noColor,
  11.110 +            let cell = tableView.dequeueReusableCell(withIdentifier: resetCellIdentifier, for: indexPath)
  11.111 +                as? TrustManagementResetTableViewCell {
  11.112 +            cell.delegate = self
  11.113 +            cell.partnerNameLabel.text = row.name
  11.114 +            viewModel?.getImage(forRowAt: indexPath, complete: { (image) in
  11.115 +                DispatchQueue.main.async {
  11.116 +                    cell.partnerImageView.image = image
  11.117 +                }
  11.118 +            })
  11.119 +            return cell
  11.120 +        }
  11.121 +         
  11.122 +        /// Cell ´no-noColor´ context
  11.123 +        let identifier : String
  11.124 +        if UIDevice.current.orientation.isLandscape ||
  11.125 +            UIDevice.current.userInterfaceIdiom == .pad {
  11.126 +            identifier = masterAndDetailCellIdentifier
  11.127 +        } else {
  11.128 +            identifier = onlyMasterCellIdentifier
  11.129 +        }
  11.130 +
  11.131 +        guard let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
  11.132 +            as? TrustManagementTableViewCell else {
  11.133 +                Log.shared.error("The TrustManagementTableViewCell couldn't be dequeued")
  11.134 +                return UITableViewCell()
  11.135 +        }
  11.136 +        viewModel?.getImage(forRowAt: indexPath, complete: { (image) in
  11.137 +            DispatchQueue.main.async {
  11.138 +                cell.partnerImageView.image = image
  11.139 +            }
  11.140 +        })
  11.141 +        cell.privacyStatusImageView.image = row.privacyStatusImage
  11.142 +        cell.partnerNameLabel.text = row.name
  11.143 +        cell.privacyStatusLabel.text = row.privacyStatusName
  11.144 +        cell.descriptionLabel.text = row.description
  11.145 +        configureTrustwords(identifier, row, cell, indexPath)
  11.146 +        cell.delegate = self
  11.147 +        return cell
  11.148 +    }
  11.149 +}
  11.150 +
  11.151 +/// MARK: - UIAlertControllers
  11.152 +
  11.153 +extension TrustManagementViewController {
  11.154 +    
  11.155 +    /// This should only be used if the flow comes from the Compose View.
  11.156 +    private func presentToogleProtectionActionSheet() {
  11.157 +        guard let viewModel = viewModel else {
  11.158 +            Log.shared.errorAndCrash("View Model must not be nil")
  11.159 +            return
  11.160 +        }
  11.161 +        let alertController = UIAlertController.pEpAlertController(title: nil, message: nil,
  11.162 +                                                                   preferredStyle: .actionSheet)
  11.163 +        let enable = NSLocalizedString("Enable Protection", comment: "Enable Protection")
  11.164 +        let disable = NSLocalizedString("Disable Protection", comment: "Disable Protection")
  11.165 +        let toogleProtectionTitle = viewModel.pEpProtected  ? disable : enable
  11.166 +        let action = UIAlertAction(title: toogleProtectionTitle, style: .default) { (action) in
  11.167 +            viewModel.handleToggleProtectionPressed()
  11.168 +        }
  11.169 +        alertController.addAction(action)
  11.170 +        let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
  11.171 +        let cancelAction = UIAlertAction(title:cancelTitle , style: .cancel) { _ in
  11.172 +            alertController.dismiss(animated: true, completion: nil)
  11.173 +        }
  11.174 +        alertController.addAction(cancelAction)
  11.175 +        
  11.176 +        /// A broken contraint comes up, it's a known issue in iOS.
  11.177 +        /// https://github.com/lionheart/openradar-mirror/issues/21120
  11.178 +        if let buttonView = optionsButton.value(forKey: "view") as? UIView {
  11.179 +            alertController.popoverPresentationController?.sourceView = buttonView
  11.180 +            alertController.popoverPresentationController?.sourceRect = buttonView.bounds
  11.181 +        }
  11.182 +        present(alertController, animated: true, completion: nil)
  11.183 +    }
  11.184 +    
  11.185 +    /// Shows an action sheet with languages when the user taps the language button from a cell
  11.186 +    /// - Parameter cell: The cell of the language button tapped.
  11.187 +    private func showLanguagesList(for cell: TrustManagementTableViewCell) {
  11.188 +        guard let indexPath = tableView.indexPath(for: cell) else {
  11.189 +            Log.shared.error("IndexPath not found")
  11.190 +            return
  11.191 +        }
  11.192 +        let alertController = UIAlertController.pEpAlertController(title: nil,
  11.193 +                                                                   message: nil,
  11.194 +                                                                   preferredStyle: .actionSheet)
  11.195 +        guard let languages = viewModel?.handleChangeLanguagePressed(forRowAt: indexPath) else {
  11.196 +            Log.shared.error("Languages not found")
  11.197 +            return
  11.198 +        }
  11.199 +        //For every language a row in the action sheet.
  11.200 +        for language in languages {
  11.201 +            guard let languageName = NSLocale.current.localizedString(forLanguageCode: language)
  11.202 +                else {
  11.203 +                    Log.shared.debug("Language name not found")
  11.204 +                    break
  11.205 +            }
  11.206 +            let action = UIAlertAction(title: languageName, style: .default) { [weak self] (action) in
  11.207 +                guard let me = self else {
  11.208 +                    Log.shared.error("Lost myself")
  11.209 +                    return
  11.210 +                }
  11.211 +                me.viewModel?.didSelectLanguage(forRowAt: indexPath,
  11.212 +                                                language: language)
  11.213 +            }
  11.214 +            alertController.addAction(action)
  11.215 +        }
  11.216 +        
  11.217 +        //For the cancel button another action.
  11.218 +        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in
  11.219 +            alertController.dismiss(animated: true, completion: nil)
  11.220 +        }
  11.221 +        alertController.addAction(cancelAction)
  11.222 +
  11.223 +        //Ipad behavior.
  11.224 +        alertController.popoverPresentationController?.sourceView = cell.languageButton
  11.225 +        alertController.popoverPresentationController?.sourceRect = cell.languageButton.bounds
  11.226 +        present(alertController, animated: true, completion: nil)
  11.227 +    }
  11.228 +}
  11.229 +
  11.230 +/// MARK: - Handshake ViewModel Delegate
  11.231 +
  11.232 +extension TrustManagementViewController: TrustManagementViewModelDelegate {
  11.233 +    
  11.234 +    @objc public func reload() {
  11.235 +        UIView.setAnimationsEnabled(false)
  11.236 +        tableView.reloadData()
  11.237 +        UIView.setAnimationsEnabled(true)
  11.238 +    }
  11.239 +}
  11.240 +
  11.241 +/// MARK: - Back button
  11.242 +
  11.243 +extension TrustManagementViewController {
  11.244 +    
  11.245 +    /// Helper method to create and set the back button in the navigation bar.
  11.246 +    private func setLeftBarButton() {
  11.247 +        let title = NSLocalizedString(" Message",
  11.248 +                                      comment: "TrustManagementView Back Button Title")
  11.249 +        let button = UIButton.backButton(with: title)
  11.250 +        let action = #selector(backButtonPressed)
  11.251 +        button.addTarget(self, action:action, for: .touchUpInside)
  11.252 +        let leftItem = UIBarButtonItem(customView: button)
  11.253 +        navigationItem.leftBarButtonItem = leftItem
  11.254 +        navigationController?.navigationBar.isTranslucent = false
  11.255 +    }
  11.256 +
  11.257 +    @objc private func backButtonPressed() {
  11.258 +        dismiss(animated: true, completion: nil)
  11.259 +    }
  11.260 +}
  11.261 +
  11.262 +/// MARK: - Notification Center
  11.263 +
  11.264 +extension TrustManagementViewController {
  11.265 +    
  11.266 +    private func registerForNotifications() {
  11.267 +        NotificationCenter.default.addObserver(self, selector: #selector(reload),
  11.268 +                                               name: UIDevice.orientationDidChangeNotification,
  11.269 +                                               object: nil)
  11.270 +    }
  11.271 +    
  11.272 +    private func unregisterNotifications() {
  11.273 +        NotificationCenter.default.removeObserver(self)
  11.274 +    }
  11.275 +}
  11.276 +
  11.277 +/// MARK: - Set trustwords
  11.278 +
  11.279 +extension TrustManagementViewController {
  11.280 +    
  11.281 +    /// Generates and sets the trustwords to the cell
  11.282 +    /// - Parameters:
  11.283 +    ///   - cell: The cell where the trustwords would be setted
  11.284 +    ///   - indexPath: The indexPath of the row to generate the trustwords.
  11.285 +    ///   - longMode: Indicates if the trustwords have to be long.
  11.286 +    private func setTrustwords(for cell: TrustManagementTableViewCell,
  11.287 +                               at indexPath: IndexPath,
  11.288 +                               longMode: Bool) {
  11.289 +        guard let vm = viewModel else {
  11.290 +            Log.shared.errorAndCrash("No VM")
  11.291 +            return
  11.292 +        }
  11.293 +        vm.generateTrustwords(forRowAt: indexPath, long: longMode) { [weak self] trustwords in
  11.294 +            guard let trustwords = trustwords else {
  11.295 +                Log.shared.debug("Trustwords are nil. The view must not be updated")
  11.296 +                return
  11.297 +            }
  11.298 +            guard let me = self else {
  11.299 +                Log.shared.error("Lost myself")
  11.300 +                return
  11.301 +            }
  11.302 +
  11.303 +            let oneSpace = " "
  11.304 +            let threeSpaces = "   "
  11.305 +            let spacedTrustwords = trustwords.replacingOccurrences(of: oneSpace, with: threeSpaces)
  11.306 +            let textToSet = longMode ? spacedTrustwords : "\(spacedTrustwords)…"
  11.307 +            if (cell.trustwordsLabel.text != textToSet) {
  11.308 +                cell.trustwordsLabel.text = textToSet
  11.309 +                me.tableView.updateSize()
  11.310 +            }
  11.311 +        }
  11.312 +    }
  11.313 +}
  11.314 +
  11.315 +/// MARK: - TrustManagementTableViewCellDelegate
  11.316 +
  11.317 +extension TrustManagementViewController: TrustManagementTableViewCellDelegate,
  11.318 +TrustManagementResetTableViewCellDelegate {
  11.319 +    func languageButtonPressed(on cell: TrustManagementTableViewCell) {
  11.320 +        showLanguagesList(for: cell)
  11.321 +    }
  11.322 +    
  11.323 +    func declineButtonPressed(on cell: TrustManagementTableViewCell) {
  11.324 +        guard let viewModel = viewModel else {
  11.325 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  11.326 +            return
  11.327 +        }
  11.328 +        if let indexPath = tableView.indexPath(for: cell) {
  11.329 +            viewModel.handleRejectHandshakePressed(at: indexPath)
  11.330 +        }
  11.331 +    }
  11.332 +    
  11.333 +    func confirmButtonPressed(on cell: TrustManagementTableViewCell) {
  11.334 +        guard let viewModel = viewModel else {
  11.335 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  11.336 +            return
  11.337 +        }
  11.338 +
  11.339 +        if let indexPath = tableView.indexPath(for: cell) {
  11.340 +            viewModel.handleConfirmHandshakePressed(at: indexPath)
  11.341 +        }
  11.342 +    }
  11.343 +    
  11.344 +    func resetButtonPressed(on cell: UITableViewCell) {
  11.345 +        guard let viewModel = viewModel else {
  11.346 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  11.347 +            return
  11.348 +        }
  11.349 +        if let indexPath = tableView.indexPath(for: cell) {
  11.350 +            viewModel.handleResetPressed(forRowAt: indexPath)
  11.351 +        }
  11.352 +    }
  11.353 +    
  11.354 +    func trustwordsLabelPressed(on cell: TrustManagementTableViewCell) {
  11.355 +        guard let viewModel = viewModel else {
  11.356 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  11.357 +            return
  11.358 +        }
  11.359 +        if let indexPath = tableView.indexPath(for: cell) {
  11.360 +            viewModel.handleToggleLongTrustwords(forRowAt: indexPath)
  11.361 +        }
  11.362 +    }
  11.363 +}
  11.364 +
  11.365 +/// MARK: - Cell configuration
  11.366 +
  11.367 +extension TrustManagementViewController {
  11.368 +    
  11.369 +    /// This method configures the layout for the provided cell.
  11.370 +    /// We use 2 different cells: one for the split view the other for iphone portrait view.
  11.371 +    /// The layout is different, so different UI structures are used.
  11.372 +    /// 
  11.373 +    /// - Parameters:
  11.374 +    ///   - identifier: As we handle two different cells, the identifier is required in order to set the layout properly.
  11.375 +    ///   - row: The row to get information to configure the cell.
  11.376 +    ///   - cell: The cell to be configured.
  11.377 +    ///   - indexPath: The indexPath of the row, to get the trustwords.
  11.378 +    private func configureTrustwords(_ identifier: String, _ row: TrustManagementViewModel.Row, _ cell: TrustManagementTableViewCell, _ indexPath: IndexPath) {
  11.379 +        ///Yellow means secure but not trusted.
  11.380 +        ///That means that's the only case must display the trustwords
  11.381 +        if identifier == onlyMasterCellIdentifier {
  11.382 +            if row.color == .yellow {
  11.383 +                setTrustwords(for: cell, at: indexPath, longMode: row.longTrustwords)
  11.384 +                cell.trustwordsStackView.isHidden = false
  11.385 +                cell.trustwordsButtonsContainer.isHidden = false
  11.386 +            } else {
  11.387 +                cell.trustwordsStackView.isHidden = true
  11.388 +                cell.trustwordsButtonsContainer.isHidden = true
  11.389 +            }
  11.390 +        } else if identifier == masterAndDetailCellIdentifier {
  11.391 +            if row.color == .yellow {
  11.392 +                setTrustwords(for: cell, at: indexPath, longMode: row.longTrustwords)
  11.393 +                cell.trustwordsLabel.isHidden = false
  11.394 +                cell.confirmButton.isHidden = false
  11.395 +                cell.declineButton.isHidden = false
  11.396 +                cell.languageButton.isHidden = false
  11.397 +            } else {
  11.398 +                cell.languageButton.isHidden = true
  11.399 +                cell.trustwordsLabel.isHidden = true
  11.400 +                cell.confirmButton.isHidden = true
  11.401 +                cell.declineButton.isHidden = true
  11.402 +            }
  11.403 +        }
  11.404 +    }
  11.405 +}
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/pEpForiOS/UI/TrustManagement/TrustManagementViewModel.swift	Wed Mar 11 19:09:12 2020 +0100
    12.3 @@ -0,0 +1,382 @@
    12.4 +//
    12.5 +//  TrustManagementViewModel.swift
    12.6 +//  pEp
    12.7 +//
    12.8 +//  Created by Martin Brude on 30/01/2020.
    12.9 +//  Copyright © 2020 p≡p Security S.A. All rights reserved.
   12.10 +//
   12.11 +
   12.12 +import Foundation
   12.13 +import MessageModel
   12.14 +import PEPObjCAdapterFramework
   12.15 +
   12.16 +/// TrustManagementViewModel View Mode Delegate
   12.17 +protocol TrustManagementViewModelDelegate: class {
   12.18 +    /// Delegate method to notify that an action ends and the view must be reloaded.
   12.19 +    func reload()
   12.20 +}
   12.21 +
   12.22 +protocol TrustmanagementProtectionStateChangeDelegate: class {
   12.23 +    /// Called whenever the user toggles protection state (for the message)
   12.24 +    func protectionStateChanged(to newValue: Bool)
   12.25 +}
   12.26 +
   12.27 +extension TrustManagementViewModel {
   12.28 +    /// The item that represents the handshake partner
   12.29 +    public struct Row {
   12.30 +        /// Indicates the handshake partner's name
   12.31 +        var name: String {
   12.32 +            let name = handshakeCombination.partnerIdentity.userName
   12.33 +            let address = handshakeCombination.partnerIdentity.address
   12.34 +            return name ?? address
   12.35 +        }
   12.36 +        /// The description for the row
   12.37 +        var description: String {
   12.38 +            if forceRed {
   12.39 +                return PEPColor.red.privacyStatusDescription
   12.40 +            }
   12.41 +            return color.privacyStatusDescription
   12.42 +        }
   12.43 +        /// The privacy status name
   12.44 +        var privacyStatusName: String {
   12.45 +            if (forceRed) {
   12.46 +                return String.trustIdentityTranslation(pEpRating: .underAttack).title
   12.47 +            }
   12.48 +            let rating = handshakeCombination.partnerIdentity.pEpRating()
   12.49 +            let translations = String.trustIdentityTranslation(pEpRating: rating)
   12.50 +            return translations.title
   12.51 +        }
   12.52 +        /// The privacy status image
   12.53 +        var privacyStatusImage: UIImage? {
   12.54 +            if forceRed {
   12.55 +                return PEPColor.red.statusIconForMessage(enabled: true, withText: false)
   12.56 +            }
   12.57 +            return color.statusIconForMessage(enabled: true, withText: false)
   12.58 +        }
   12.59 +        /// The current language
   12.60 +        var currentLanguage: String
   12.61 +        /// Indicates if the trustwords are long
   12.62 +        var longTrustwords: Bool = false
   12.63 +        /// The privacy status in between the current user and the partner
   12.64 +        var privacyStatus: String?
   12.65 +        /// Status indicator
   12.66 +        var color : PEPColor {
   12.67 +            if forceRed {
   12.68 +                return PEPColor.red
   12.69 +            }
   12.70 +            return handshakeCombination.partnerIdentity.pEpColor()
   12.71 +        }
   12.72 +        var trustwords : String?
   12.73 +        //Prevents the overkill of require the trustwords when it's not necesary.
   12.74 +        fileprivate var shouldUpdateTrustwords : Bool = true
   12.75 +        fileprivate var forceRed: Bool = false
   12.76 +        /// The identity of the user to do the handshake
   12.77 +        fileprivate var handshakeCombination: TrustManagementUtil.HandshakeCombination
   12.78 +        fileprivate var fingerprint: String?
   12.79 +    }
   12.80 +}
   12.81 +
   12.82 +/// View Model to handle the TrustManagementViewModel views.
   12.83 +final class TrustManagementViewModel {
   12.84 +    weak public var delegate : TrustManagementViewModelDelegate?
   12.85 +    weak public var protectionStateChangeDelegate: TrustmanagementProtectionStateChangeDelegate?
   12.86 +    public var pEpProtected : Bool {
   12.87 +        didSet {
   12.88 +            protectionStateChangeDelegate?.protectionStateChanged(to: pEpProtected)
   12.89 +        }
   12.90 +    }
   12.91 +    var shouldShowOptionsButton: Bool = false
   12.92 +    private var message: Message
   12.93 +    private var trustManagementUtil : TrustManagementUtilProtocol?
   12.94 +    private let undoManager = UndoManager()
   12.95 +    private var actionPerformed = [String]()
   12.96 +    
   12.97 +    /// Items to be displayed in the View Controller
   12.98 +    private (set) var rows: [Row] = [Row]()
   12.99 +
  12.100 +    /// Constructor
  12.101 +    /// - Parameters:
  12.102 +    ///   - message: The message to manage the trust
  12.103 +    ///   - handshakeUtil: The tool to interact with the engine. It provides a default instance. The parameter is used for testing purposes.
  12.104 +    public init(message : Message,
  12.105 +                pEpProtectionModifyable: Bool,
  12.106 +                delegate : TrustManagementViewModelDelegate? = nil,
  12.107 +                protectionStateChangeDelegate: TrustmanagementProtectionStateChangeDelegate? = nil,
  12.108 +                trustManagementUtil: TrustManagementUtilProtocol? = TrustManagementUtil()) {
  12.109 +        self.message = message
  12.110 +        self.trustManagementUtil = trustManagementUtil
  12.111 +        self.pEpProtected = message.pEpProtected
  12.112 +        self.shouldShowOptionsButton = pEpProtectionModifyable
  12.113 +        self.delegate = delegate
  12.114 +        self.protectionStateChangeDelegate = protectionStateChangeDelegate
  12.115 +        generateRows()
  12.116 +    }
  12.117 +
  12.118 +    ///MARK - Actions
  12.119 +    
  12.120 +    /// Reject the handshake
  12.121 +    /// - Parameter indexPath: The indexPath of the item to get the user to reject the handshake
  12.122 +    public func handleRejectHandshakePressed(at indexPath: IndexPath) {
  12.123 +        guard let trustManagementViewModel = trustManagementUtil else {
  12.124 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  12.125 +            return
  12.126 +        }
  12.127 +        let actionName = NSLocalizedString("Trust Rejection", comment: "Action name to be suggested at the moment of revert")
  12.128 +        actionPerformed.append(actionName)
  12.129 +        registerUndoAction(at: indexPath)
  12.130 +        let row = rows[indexPath.row]
  12.131 +        let identity : Identity = row.handshakeCombination.partnerIdentity.safeForSession(Session.main)
  12.132 +        rows[indexPath.row].fingerprint = trustManagementViewModel.getFingerprint(for: identity)
  12.133 +        rows[indexPath.row].forceRed = true
  12.134 +        trustManagementViewModel.denyTrust(for: identity)
  12.135 +        reevaluateMessage()
  12.136 +        delegate?.reload()
  12.137 +    }
  12.138 +    
  12.139 +    /// Confirm the handshake
  12.140 +    /// - Parameter indexPath: The indexPath of the item to get the user to confirm the handshake
  12.141 +    public func handleConfirmHandshakePressed(at indexPath: IndexPath) {
  12.142 +        guard let trustManagementViewModel = trustManagementUtil else {
  12.143 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  12.144 +            return
  12.145 +        }
  12.146 +        let actionName = NSLocalizedString("Trust Confirmation", comment: "Action name to be suggested at the moment of revert")
  12.147 +        actionPerformed.append(actionName)
  12.148 +        registerUndoAction(at: indexPath)
  12.149 +        let row = rows[indexPath.row]
  12.150 +        rows[indexPath.row].forceRed = false
  12.151 +        let identity : Identity = row.handshakeCombination.partnerIdentity.safeForSession(Session.main)
  12.152 +        trustManagementViewModel.confirmTrust(for: identity)
  12.153 +        reevaluateMessage()
  12.154 +        delegate?.reload()
  12.155 +    }
  12.156 +    
  12.157 +    /// Handles the undo action.
  12.158 +    /// That means that the trust will be reseted.
  12.159 +    /// So it is not important what action in concrete was performed.
  12.160 +    /// - Parameter indexPath: The index path of the row from where the last action has been performed.
  12.161 +    @objc public func handleUndo(forRowAt indexPath: IndexPath) {
  12.162 +        guard let trustManagementViewModel = trustManagementUtil else {
  12.163 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  12.164 +            return
  12.165 +        }
  12.166 +        let row = rows[indexPath.row]
  12.167 +        rows[indexPath.row].shouldUpdateTrustwords = true
  12.168 +        rows[indexPath.row].forceRed = false
  12.169 +        trustManagementViewModel.undoMisstrustOrTrust(for: row.handshakeCombination.partnerIdentity,
  12.170 +                                                      fingerprint: row.fingerprint)
  12.171 +        reevaluateMessage()
  12.172 +    }
  12.173 +    
  12.174 +    /// Handles the redey action
  12.175 +    /// - Parameter indexPath: The indexPath of the item to get the user to undo last action.
  12.176 +    public func handleResetPressed(forRowAt indexPath: IndexPath) {
  12.177 +        guard let trustManagementViewModel = trustManagementUtil else {
  12.178 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  12.179 +            return
  12.180 +        }
  12.181 +        let row = rows[indexPath.row]
  12.182 +        rows[indexPath.row].forceRed = false
  12.183 +        trustManagementViewModel.resetTrust(for: row.handshakeCombination.partnerIdentity)
  12.184 +        reevaluateMessage()
  12.185 +        delegate?.reload()
  12.186 +    }
  12.187 +
  12.188 +    /// - returns: the available languages.
  12.189 +    public func handleChangeLanguagePressed(forRowAt indexPath : IndexPath) -> [String] {
  12.190 +        guard let trustManagementViewModel = trustManagementUtil else {
  12.191 +            Log.shared.errorAndCrash("TrustManagementViewModelDelegate is nil")
  12.192 +            return [String]()
  12.193 +        }
  12.194 +        rows[indexPath.row].shouldUpdateTrustwords = true
  12.195 +        guard let list = trustManagementViewModel.languagesList() else {
  12.196 +            Log.shared.error("The list of languages could be retrieved.")
  12.197 +            return [String]()
  12.198 +        }
  12.199 +        return list
  12.200 +    }
  12.201 +    
  12.202 +    /// Updates the selected language for that row.
  12.203 +    /// - Parameters:
  12.204 +    ///   - indexPath: The index path of the row
  12.205 +    ///   - language: The chosen language
  12.206 +    public func didSelectLanguage(forRowAt indexPath: IndexPath, language: String) {
  12.207 +        guard let trustManagementViewModelDelegate = delegate else {
  12.208 +            Log.shared.errorAndCrash("TrustManagementViewModelDelegate is nil")
  12.209 +            return
  12.210 +        }
  12.211 +        rows[indexPath.row].currentLanguage = language
  12.212 +        trustManagementViewModelDelegate.reload()
  12.213 +    }
  12.214 +    
  12.215 +    /// Toogle pEp protection status
  12.216 +    public func handleToggleProtectionPressed() {
  12.217 +        pEpProtected = !pEpProtected
  12.218 +    }
  12.219 +
  12.220 +    /// Informs if is it possible to undo an action.
  12.221 +    /// - returns: Indicates if it's possible to undo an action.
  12.222 +    public func canUndo() -> Bool {
  12.223 +        return undoManager.canUndo
  12.224 +    }
  12.225 +    
  12.226 +    /// - returns: The name of the last action performed, nil if there isn't any.
  12.227 +    public func lastActionPerformed() -> String? {
  12.228 +        return actionPerformed.last
  12.229 +    }
  12.230 +
  12.231 +    /// Method that makes the trustwords long or short (more or less trustwords in fact).
  12.232 +    /// - Parameter indexPath: The indexPath to get the row to toogle the status (long/short)
  12.233 +    public func handleToggleLongTrustwords(forRowAt indexPath: IndexPath) {
  12.234 +        guard let trustManagementViewModelDelegate = delegate else {
  12.235 +            Log.shared.errorAndCrash("TrustManagementViewModelDelegate is nil")
  12.236 +            return
  12.237 +        }
  12.238 +        rows[indexPath.row].shouldUpdateTrustwords = true
  12.239 +        rows[indexPath.row].longTrustwords.toggle()
  12.240 +        trustManagementViewModelDelegate.reload()
  12.241 +    }
  12.242 +
  12.243 +    public typealias TrustWords = String
  12.244 +
  12.245 +    /// Generate the trustwords
  12.246 +    /// - Parameters:
  12.247 +    ///   - indexPath: The indexPath of the row to toogle the status (long/short)
  12.248 +    ///   - long: Indicates if the trustwords MUST be long (more words)
  12.249 +    ///   - completion: the completion block to be executed once the trustwords are generated.
  12.250 +    /// If the trustwords passed are nil the UI must no be updated.
  12.251 +    /// If the completion block is nil, only the model in the row at the provided indexPath will be updated.
  12.252 +    public func generateTrustwords(forRowAt indexPath: IndexPath,
  12.253 +                                   long : Bool = false,
  12.254 +                                   completion: ((TrustWords?) -> Void)?)  {
  12.255 +        guard rows[indexPath.row].shouldUpdateTrustwords else {
  12.256 +            if let trustwords = rows[indexPath.row].trustwords {
  12.257 +                completion?(trustwords)
  12.258 +                return
  12.259 +            }
  12.260 +            completion?(nil)
  12.261 +            return
  12.262 +        }
  12.263 +        rows[indexPath.row].shouldUpdateTrustwords = false
  12.264 +        DispatchQueue.global(qos: .userInteractive).async { [weak self] in
  12.265 +            guard let me = self,
  12.266 +                let trustManagementViewModel = me.trustManagementUtil else {
  12.267 +                completion?(nil)
  12.268 +                Log.shared.errorAndCrash("Lost myself or the trustManagement ViewModel")
  12.269 +                return
  12.270 +            }
  12.271 +            let complete: (TrustWords?) -> Void = { trustwords in
  12.272 +                DispatchQueue.main.async {
  12.273 +                    completion?(trustwords)
  12.274 +                }
  12.275 +            }
  12.276 +            let handshakeItem = me.rows[indexPath.row]
  12.277 +            handshakeItem.handshakeCombination.ownIdentity.session.performAndWait {
  12.278 +                let selfIdentity = handshakeItem.handshakeCombination.ownIdentity
  12.279 +                let partnerIdentity = handshakeItem.handshakeCombination.partnerIdentity
  12.280 +                guard let trustwords = try? trustManagementViewModel.getTrustwords(for: selfIdentity,
  12.281 +                                                                                   and: partnerIdentity,
  12.282 +                                                                                   language: handshakeItem.currentLanguage,
  12.283 +                                                                                   long: long)
  12.284 +                    else {
  12.285 +                        Log.shared.errorAndCrash("No Trustwords")
  12.286 +                        completion?(nil)
  12.287 +                        return
  12.288 +                }
  12.289 +                me.rows[indexPath.row].trustwords = trustwords
  12.290 +                complete(trustwords)
  12.291 +            }
  12.292 +        }
  12.293 +    }
  12.294 +
  12.295 +    /// Method that reverts the last action performed by the user
  12.296 +    /// After the execution of this method there won't be any action to un-do.
  12.297 +    public func shakeMotionDidEnd() {
  12.298 +       /// Evaluate it the undo manager can undo means if it has something registerd to undo.
  12.299 +        /// If so, undo it and reload the view.
  12.300 +        if (undoManager.canUndo) {
  12.301 +            undoManager.undo()
  12.302 +            delegate?.reload()
  12.303 +            _ = actionPerformed.popLast()
  12.304 +        }
  12.305 +    }
  12.306 +
  12.307 +    ///MARK: - Private
  12.308 +
  12.309 +    /// This must be called after every trust state change. The curently processed message might
  12.310 +    /// change color.
  12.311 +    private func reevaluateMessage() {
  12.312 +        message.session.performAndWait { [weak self] in
  12.313 +            guard let me = self else {
  12.314 +                Log.shared.error("Lost myself - The message will not be reevaluated")
  12.315 +                return
  12.316 +            }
  12.317 +            RatingReEvaluator.reevaluate(message: me.message)
  12.318 +        }
  12.319 +    }
  12.320 +
  12.321 +    /// Method that generates the rows to be used by the VC
  12.322 +    private func generateRows() {
  12.323 +        guard let trustManagementViewModel = trustManagementUtil else {
  12.324 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  12.325 +            return
  12.326 +        }
  12.327 +        let combinations = trustManagementViewModel.handshakeCombinations(message: message)
  12.328 +
  12.329 +        for (index, combination) in combinations.enumerated() {
  12.330 +            let backupLanguage = "en"
  12.331 +            let language =
  12.332 +            combination.partnerIdentity.language ?? Locale.current.languageCode ?? backupLanguage
  12.333 +            let row = Row(currentLanguage: language,
  12.334 +                          longTrustwords: false,
  12.335 +                          handshakeCombination: combination)
  12.336 +            rows.append(row)
  12.337 +            let indexPath = IndexPath(row: index, section: 0)
  12.338 +            generateTrustwords(forRowAt: indexPath, completion: nil)
  12.339 +        }
  12.340 +    }
  12.341 +
  12.342 +    /// Register the action to be undone
  12.343 +    /// Is not important determine what action in concret must be undone.
  12.344 +    /// The trust management util -and thus, the engine- will take care of that.
  12.345 +    /// - Parameter indexPath: The indexPath of the row which the action to undo.
  12.346 +    private func registerUndoAction(at indexPath: IndexPath) {
  12.347 +        undoManager.registerUndo(withTarget: self,
  12.348 +                                 selector: #selector(handleUndo(forRowAt:)),
  12.349 +                                 object: indexPath)
  12.350 +    }
  12.351 +}
  12.352 +
  12.353 +/// MARK: - Image 
  12.354 +
  12.355 +extension TrustManagementViewModel {
  12.356 +    
  12.357 +    /// Method that returns the user image for the current indexPath throught the callback
  12.358 +    /// - Parameters:
  12.359 +    ///   - indexPath: The index path to get the user image
  12.360 +    ///   - complete: The callback with the image
  12.361 +    public func getImage(forRowAt indexPath: IndexPath, complete: @escaping (UIImage?) -> ()) {
  12.362 +        
  12.363 +        //Check if it's cached, use it if so.
  12.364 +        let handshakeItem : Row = rows[indexPath.row]
  12.365 +        let partnerIdentity = handshakeItem.handshakeCombination.partnerIdentity
  12.366 +        let contactImageTool = IdentityImageTool()
  12.367 +        let key = IdentityImageTool.IdentityKey(identity: partnerIdentity)
  12.368 +        if let cachedContactImage = contactImageTool.cachedIdentityImage(for: key) {
  12.369 +            complete(cachedContactImage)
  12.370 +            return
  12.371 +        }
  12.372 +        
  12.373 +        //If can't find the image in the cache, creates it from the session.
  12.374 +        let session = Session()
  12.375 +        let safePartnerIdentity = partnerIdentity.safeForSession(session)
  12.376 +        DispatchQueue.global(qos: .userInteractive).async {
  12.377 +            session.performAndWait {
  12.378 +                if let contactImage = contactImageTool.identityImage(for:
  12.379 +                    IdentityImageTool.IdentityKey(identity: safePartnerIdentity)) {
  12.380 +                    complete(contactImage)
  12.381 +                }
  12.382 +            }
  12.383 +        }
  12.384 +    }
  12.385 +}