renames group. HandshakeVC does not exist any more
authorbuff <andreas@pep-project.org>
Wed, 11 Mar 2020 18:30:53 +0100
changeset 12210036aff9df0b8
parent 12206 1553db76ee19
child 12211 402fdfa0698c
renames group. HandshakeVC does not exist any more
pEpForiOS.xcodeproj/project.pbxproj
pEpForiOS/UI/Handshake/CellsAndSections/TrustManagementResetTableViewCell.swift
pEpForiOS/UI/Handshake/CellsAndSections/TrustManagementTableViewCell.swift
pEpForiOS/UI/Handshake/CellsAndSections/TrustManagementTableViewCellProtocols.swift
pEpForiOS/UI/Handshake/TrustManagementViewController.swift
pEpForiOS/UI/Handshake/TrustManagementViewModel.swift
pEpForiOS/UI/TrustManagement/CellsAndSections/TrustManagementResetTableViewCell.swift
pEpForiOS/UI/TrustManagement/CellsAndSections/TrustManagementTableViewCell.swift
pEpForiOS/UI/TrustManagement/CellsAndSections/TrustManagementTableViewCellProtocols.swift
pEpForiOS/UI/TrustManagement/TrustManagementViewController.swift
pEpForiOS/UI/TrustManagement/TrustManagementViewModel.swift
     1.1 --- a/pEpForiOS.xcodeproj/project.pbxproj	Wed Mar 11 17:55:33 2020 +0100
     1.2 +++ b/pEpForiOS.xcodeproj/project.pbxproj	Wed Mar 11 18:30:53 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/UI/Handshake/CellsAndSections/TrustManagementResetTableViewCell.swift	Wed Mar 11 17:55:33 2020 +0100
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,35 +0,0 @@
     2.4 -//
     2.5 -//  TrustManagementResetTableViewCell.swift
     2.6 -//  pEp
     2.7 -//
     2.8 -//  Created by Martin Brude on 17/02/2020.
     2.9 -//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    2.10 -//
    2.11 -
    2.12 -import UIKit
    2.13 -
    2.14 -final class TrustManagementResetTableViewCell: UITableViewCell {
    2.15 -
    2.16 -    @IBOutlet weak var partnerNameLabel: UILabel!
    2.17 -    @IBOutlet weak var partnerImageView: UIImageView!
    2.18 -    @IBOutlet weak var resetLabel: UILabel!
    2.19 -    @IBOutlet weak var resetButton: UIButton!
    2.20 -    
    2.21 -    weak var delegate : TrustManagementResetTableViewCellDelegate?
    2.22 -
    2.23 -    override func awakeFromNib() {
    2.24 -        super.awakeFromNib()
    2.25 -        
    2.26 -           //Reset Button
    2.27 -           resetButton.pEpIfyForTrust(backgroundColor: .pEpGrayBackgroundReset, textColor: .white)
    2.28 -        
    2.29 -           //Reset label
    2.30 -           resetLabel.text = NSLocalizedString("Reset all p≡p data for this comunication partner:",
    2.31 -                                               comment: "Reset all p≡p data for this comunication partner:")
    2.32 -
    2.33 -    }
    2.34 -
    2.35 -    @IBAction private func resetButtonPressed() {
    2.36 -        delegate?.resetButtonPressed(on: self)
    2.37 -    }
    2.38 -}
     3.1 --- a/pEpForiOS/UI/Handshake/CellsAndSections/TrustManagementTableViewCell.swift	Wed Mar 11 17:55:33 2020 +0100
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,104 +0,0 @@
     3.4 -//
     3.5 -//  HandshakePartnerTableViewCell.swift
     3.6 -//  pEp
     3.7 -//
     3.8 -//  Created by Martin Brude on 07/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 -/// UITableViewCell for trust management screen
    3.15 -final class TrustManagementTableViewCell: UITableViewCell {
    3.16 - 
    3.17 -    //Content
    3.18 -    @IBOutlet weak var partnerImageView: UIImageView!
    3.19 -    @IBOutlet weak var privacyStatusImageView: UIImageView!
    3.20 -    @IBOutlet weak var partnerNameLabel: UILabel!
    3.21 -    @IBOutlet weak var privacyStatusLabel: UILabel!
    3.22 -    @IBOutlet weak var trustwordsLabel: UILabel!
    3.23 -    @IBOutlet weak var descriptionLabel: UILabel!
    3.24 -    @IBOutlet weak var languageButton: UIButton!
    3.25 -    
    3.26 -    //Only for i18n and layout
    3.27 -    @IBOutlet weak var resetLabel: UILabel!
    3.28 -    @IBOutlet weak var declineButton: UIButton!
    3.29 -    @IBOutlet weak var confirmButton: UIButton!
    3.30 -    @IBOutlet weak var resetButton: UIButton!
    3.31 -    
    3.32 -    //Hide these views in case pepColor is not yellow.
    3.33 -    @IBOutlet weak var trustwordsStackView: UIStackView!
    3.34 -    @IBOutlet weak var trustwordsButtonsContainer: UIView!
    3.35 -    
    3.36 -    weak var delegate : TrustManagementTableViewCellDelegate?
    3.37 -    
    3.38 -    override func awakeFromNib() {
    3.39 -        super.awakeFromNib()
    3.40 -        setupView()
    3.41 -    }
    3.42 -    
    3.43 -    /// Reset attributes of the cell that are not related to content.
    3.44 -    override func prepareForReuse() {
    3.45 -        super.prepareForReuse()
    3.46 -        removeGestureRecognizers()
    3.47 -    }
    3.48 -
    3.49 -    // MARK: - Actions
    3.50 -    
    3.51 -    @IBAction private func languageButtonPressed() {
    3.52 -        delegate?.languageButtonPressed(on: self)
    3.53 -    }
    3.54 -    
    3.55 -    @IBAction private func declineButtonPressed() {
    3.56 -        delegate?.declineButtonPressed(on: self)
    3.57 -    }
    3.58 -
    3.59 -    @IBAction private func confirmButtonPressed() {
    3.60 -        delegate?.confirmButtonPressed(on: self)
    3.61 -    }
    3.62 -
    3.63 -    @IBAction private func resetButtonPressed() {
    3.64 -        delegate?.resetButtonPressed(on: self)
    3.65 -    }
    3.66 -    
    3.67 -    @objc private func trustwordsLabelPressed() {
    3.68 -        delegate?.trustwordsLabelPressed(on: self)
    3.69 -    }
    3.70 -    
    3.71 -    // MARK: - Private
    3.72 -    
    3.73 -    /// Setup the view with the row data.
    3.74 -    private func setupView() {
    3.75 -        removeGestureRecognizers()
    3.76 -
    3.77 -        let gesture = UITapGestureRecognizer(target: self, action: #selector(trustwordsLabelPressed))
    3.78 -        trustwordsLabel.addGestureRecognizer(gesture)
    3.79 -    
    3.80 -        //Confirm Button
    3.81 -        let confirmTitle = NSLocalizedString("Confirm", comment: "Confirm correct trustwords/PGP fingerprint")
    3.82 -        confirmButton.setTitle(confirmTitle, for: .normal)
    3.83 -        confirmButton.pEpIfyForTrust(backgroundColor: .pEpGreen, textColor: .white)
    3.84 -        
    3.85 -        //Decline Button
    3.86 -        let declineTitle = NSLocalizedString("Decline", comment: "Incorrect trustwords/PGP fingerprint")
    3.87 -        declineButton.setTitle(declineTitle, for: .normal)
    3.88 -        declineButton.pEpIfyForTrust(backgroundColor: .pEpRed, textColor: .white)
    3.89 -
    3.90 -        //Reset Button
    3.91 -        resetButton.pEpIfyForTrust(backgroundColor: .pEpGrayBackgroundReset, textColor: .white)
    3.92 -     
    3.93 -        //Reset label
    3.94 -        resetLabel.text = NSLocalizedString("Reset all p≡p data for this comunication partner:",
    3.95 -                                            comment: "Reset all p≡p data for this comunication partner:")
    3.96 -        //Image view
    3.97 -        partnerImageView.layer.cornerRadius = 10
    3.98 -        partnerImageView.layer.masksToBounds = true
    3.99 -    }
   3.100 -
   3.101 -    private func removeGestureRecognizers() {
   3.102 -        let existingGRs = gestureRecognizers ?? []
   3.103 -        for gr in existingGRs {
   3.104 -            removeGestureRecognizer(gr)
   3.105 -        }
   3.106 -    }
   3.107 -}
     4.1 --- a/pEpForiOS/UI/Handshake/CellsAndSections/TrustManagementTableViewCellProtocols.swift	Wed Mar 11 17:55:33 2020 +0100
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,33 +0,0 @@
     4.4 -//
     4.5 -//  TrustManagementTableViewCellProtocols.swift
     4.6 -//  pEp
     4.7 -//
     4.8 -//  Created by Martin Brude on 18/02/2020.
     4.9 -//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    4.10 -//
    4.11 -
    4.12 -import Foundation
    4.13 -
    4.14 -/// Delegate to notify the events in the cell.
    4.15 -protocol TrustManagementResetTableViewCellDelegate: class {
    4.16 -    /// Delegate method to notify the reset button has been pressed.
    4.17 -    /// - Parameter cell: The cell where the reset button has been pressed
    4.18 -    func resetButtonPressed(on cell: UITableViewCell)
    4.19 -}
    4.20 -
    4.21 -/// Delegate to notify the events in the cell.
    4.22 -protocol TrustManagementTableViewCellDelegate: TrustManagementResetTableViewCellDelegate {
    4.23 -    
    4.24 -    /// Delegate method to notify the language button has been pressed.
    4.25 -    /// - Parameter cell: The cell where the language button has been pressed
    4.26 -    func languageButtonPressed(on cell: TrustManagementTableViewCell)
    4.27 -    /// Delegate method to notify the decline button has been pressed.
    4.28 -    /// - Parameter cell: The cell where the decline button has been pressed
    4.29 -    func declineButtonPressed(on cell: TrustManagementTableViewCell)
    4.30 -    /// Delegate method to notify the confirm button has been pressed.
    4.31 -    /// - Parameter cell: The cell where the confirm button has been pressed
    4.32 -    func confirmButtonPressed(on cell: TrustManagementTableViewCell)
    4.33 -    /// Delegate method to notify the trustwords label has been pressed.
    4.34 -    /// - Parameter cell: The cell where the trustwords label has been pressed
    4.35 -    func trustwordsLabelPressed(on cell : TrustManagementTableViewCell)
    4.36 -}
     5.1 --- a/pEpForiOS/UI/Handshake/TrustManagementViewController.swift	Wed Mar 11 17:55:33 2020 +0100
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,402 +0,0 @@
     5.4 -//
     5.5 -//  TrustManagementViewController.swift
     5.6 -//  pEp
     5.7 -//
     5.8 -//  Created by Martin Brude on 05/02/2020.
     5.9 -//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    5.10 -//
    5.11 -
    5.12 -import UIKit
    5.13 -
    5.14 -/// View Controller to handle the HandshakeView.
    5.15 -class TrustManagementViewController: BaseViewController {
    5.16 -    private let onlyMasterCellIdentifier = "TrustManagementTableViewCell_OnlyMaster"
    5.17 -    private let masterAndDetailCellIdentifier = "TrustManagementTableViewCell_Detailed"
    5.18 -    private let resetCellIdentifier = "TrustManagementTableViewResetCell"
    5.19 -    @IBOutlet weak var tableView: UITableView!
    5.20 -    @IBOutlet weak var optionsButton: UIBarButtonItem!
    5.21 -
    5.22 -    var viewModel : TrustManagementViewModel?
    5.23 -
    5.24 -    override func viewDidLoad() {
    5.25 -        super.viewDidLoad()
    5.26 -        setup()
    5.27 -        tableView.rowHeight = UITableView.automaticDimension
    5.28 -        tableView.estimatedRowHeight = 400
    5.29 -    }
    5.30 -
    5.31 -    override func viewWillAppear(_ animated: Bool) {
    5.32 -        super.viewWillAppear(animated)
    5.33 -        guard let vm = viewModel else {
    5.34 -            Log.shared.errorAndCrash("The viewModel must not be nil")
    5.35 -            return
    5.36 -        }
    5.37 -        if (!vm.shouldShowOptionsButton) {
    5.38 -            navigationItem.rightBarButtonItems?.removeAll(where: {$0 == optionsButton})
    5.39 -        } else {
    5.40 -            optionsButton.title = NSLocalizedString("Options", comment: "Options")
    5.41 -        }
    5.42 -        vm.delegate = self
    5.43 -    }
    5.44 -
    5.45 -    override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
    5.46 -        guard let vm = viewModel, vm.canUndo() && motion == .motionShake,
    5.47 -            let actionName = vm.lastActionPerformed() else { return }
    5.48 -        let title = NSLocalizedString("Undo \(actionName)", comment: "Undo trust change verification alert title")
    5.49 -        let alertController = UIAlertController.pEpAlertController(title: title,
    5.50 -                                                                   message: nil,
    5.51 -                                                                   preferredStyle: .alert)
    5.52 -        let confirmTitle = NSLocalizedString("Undo", comment: "Undo trust change verification button title")
    5.53 -        let action = UIAlertAction(title: confirmTitle, style: .default) { [weak vm] (action) in
    5.54 -            vm?.shakeMotionDidEnd()
    5.55 -        }
    5.56 -        alertController.addAction(action)
    5.57 -        
    5.58 -        //For the cancel button another action.
    5.59 -        let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel trust change to be undone")
    5.60 -        let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) { _ in
    5.61 -            alertController.dismiss(animated: true, completion: nil)
    5.62 -        }
    5.63 -        alertController.addAction(cancelAction)
    5.64 -        present(alertController, animated: true, completion: nil)
    5.65 -    }
    5.66 -
    5.67 -    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    5.68 -        super.viewWillTransition(to: size, with: coordinator)
    5.69 -        tableView.reloadData()
    5.70 -    }
    5.71 -
    5.72 -    @IBAction private func optionsButtonPressed(_ sender: UIBarButtonItem) {
    5.73 -        presentToogleProtectionActionSheet()
    5.74 -    }
    5.75 -   
    5.76 -    deinit {
    5.77 -        unregisterNotifications()
    5.78 -    }
    5.79 -}
    5.80 -
    5.81 -// MARK: - Private
    5.82 -
    5.83 -extension TrustManagementViewController {
    5.84 -
    5.85 -    private func setup() {
    5.86 -        registerForNotifications()
    5.87 -        setLeftBarButton()
    5.88 -    }
    5.89 -}
    5.90 -
    5.91 -/// MARK: - UITableViewDataSource
    5.92 -
    5.93 -extension TrustManagementViewController : UITableViewDataSource  {
    5.94 -    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    5.95 -        guard let numberOfRows = viewModel?.rows.count else {
    5.96 -            Log.shared.error("The viewModel must not be nil")
    5.97 -            return 0
    5.98 -        }
    5.99 -        return numberOfRows
   5.100 -    }
   5.101 -    
   5.102 -    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   5.103 -        guard let row = viewModel?.rows[indexPath.row] else {
   5.104 -            Log.shared.error("The row couldn't be dequeued")
   5.105 -            return UITableViewCell()
   5.106 -        }
   5.107 -        
   5.108 -        /// Cell for reset
   5.109 -        if row.color == .noColor,
   5.110 -            let cell = tableView.dequeueReusableCell(withIdentifier: resetCellIdentifier, for: indexPath)
   5.111 -                as? TrustManagementResetTableViewCell {
   5.112 -            cell.delegate = self
   5.113 -            cell.partnerNameLabel.text = row.name
   5.114 -            viewModel?.getImage(forRowAt: indexPath, complete: { (image) in
   5.115 -                DispatchQueue.main.async {
   5.116 -                    cell.partnerImageView.image = image
   5.117 -                }
   5.118 -            })
   5.119 -            return cell
   5.120 -        }
   5.121 -         
   5.122 -        /// Cell ´no-noColor´ context
   5.123 -        let identifier : String
   5.124 -        if UIDevice.current.orientation.isLandscape ||
   5.125 -            UIDevice.current.userInterfaceIdiom == .pad {
   5.126 -            identifier = masterAndDetailCellIdentifier
   5.127 -        } else {
   5.128 -            identifier = onlyMasterCellIdentifier
   5.129 -        }
   5.130 -
   5.131 -        guard let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
   5.132 -            as? TrustManagementTableViewCell else {
   5.133 -                Log.shared.error("The TrustManagementTableViewCell couldn't be dequeued")
   5.134 -                return UITableViewCell()
   5.135 -        }
   5.136 -        viewModel?.getImage(forRowAt: indexPath, complete: { (image) in
   5.137 -            DispatchQueue.main.async {
   5.138 -                cell.partnerImageView.image = image
   5.139 -            }
   5.140 -        })
   5.141 -        cell.privacyStatusImageView.image = row.privacyStatusImage
   5.142 -        cell.partnerNameLabel.text = row.name
   5.143 -        cell.privacyStatusLabel.text = row.privacyStatusName
   5.144 -        cell.descriptionLabel.text = row.description
   5.145 -        configureTrustwords(identifier, row, cell, indexPath)
   5.146 -        cell.delegate = self
   5.147 -        return cell
   5.148 -    }
   5.149 -}
   5.150 -
   5.151 -/// MARK: - UIAlertControllers
   5.152 -
   5.153 -extension TrustManagementViewController {
   5.154 -    
   5.155 -    /// This should only be used if the flow comes from the Compose View.
   5.156 -    private func presentToogleProtectionActionSheet() {
   5.157 -        guard let viewModel = viewModel else {
   5.158 -            Log.shared.errorAndCrash("View Model must not be nil")
   5.159 -            return
   5.160 -        }
   5.161 -        let alertController = UIAlertController.pEpAlertController(title: nil, message: nil,
   5.162 -                                                                   preferredStyle: .actionSheet)
   5.163 -        let enable = NSLocalizedString("Enable Protection", comment: "Enable Protection")
   5.164 -        let disable = NSLocalizedString("Disable Protection", comment: "Disable Protection")
   5.165 -        let toogleProtectionTitle = viewModel.pEpProtected  ? disable : enable
   5.166 -        let action = UIAlertAction(title: toogleProtectionTitle, style: .default) { (action) in
   5.167 -            viewModel.handleToggleProtectionPressed()
   5.168 -        }
   5.169 -        alertController.addAction(action)
   5.170 -        let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
   5.171 -        let cancelAction = UIAlertAction(title:cancelTitle , style: .cancel) { _ in
   5.172 -            alertController.dismiss(animated: true, completion: nil)
   5.173 -        }
   5.174 -        alertController.addAction(cancelAction)
   5.175 -        
   5.176 -        /// A broken contraint comes up, it's a known issue in iOS.
   5.177 -        /// https://github.com/lionheart/openradar-mirror/issues/21120
   5.178 -        if let buttonView = optionsButton.value(forKey: "view") as? UIView {
   5.179 -            alertController.popoverPresentationController?.sourceView = buttonView
   5.180 -            alertController.popoverPresentationController?.sourceRect = buttonView.bounds
   5.181 -        }
   5.182 -        present(alertController, animated: true, completion: nil)
   5.183 -    }
   5.184 -    
   5.185 -    /// Shows an action sheet with languages when the user taps the language button from a cell
   5.186 -    /// - Parameter cell: The cell of the language button tapped.
   5.187 -    private func showLanguagesList(for cell: TrustManagementTableViewCell) {
   5.188 -        guard let indexPath = tableView.indexPath(for: cell) else {
   5.189 -            Log.shared.error("IndexPath not found")
   5.190 -            return
   5.191 -        }
   5.192 -        let alertController = UIAlertController.pEpAlertController(title: nil,
   5.193 -                                                                   message: nil,
   5.194 -                                                                   preferredStyle: .actionSheet)
   5.195 -        guard let languages = viewModel?.handleChangeLanguagePressed(forRowAt: indexPath) else {
   5.196 -            Log.shared.error("Languages not found")
   5.197 -            return
   5.198 -        }
   5.199 -        //For every language a row in the action sheet.
   5.200 -        for language in languages {
   5.201 -            guard let languageName = NSLocale.current.localizedString(forLanguageCode: language)
   5.202 -                else {
   5.203 -                    Log.shared.debug("Language name not found")
   5.204 -                    break
   5.205 -            }
   5.206 -            let action = UIAlertAction(title: languageName, style: .default) { [weak self] (action) in
   5.207 -                guard let me = self else {
   5.208 -                    Log.shared.error("Lost myself")
   5.209 -                    return
   5.210 -                }
   5.211 -                me.viewModel?.didSelectLanguage(forRowAt: indexPath,
   5.212 -                                                language: language)
   5.213 -            }
   5.214 -            alertController.addAction(action)
   5.215 -        }
   5.216 -        
   5.217 -        //For the cancel button another action.
   5.218 -        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in
   5.219 -            alertController.dismiss(animated: true, completion: nil)
   5.220 -        }
   5.221 -        alertController.addAction(cancelAction)
   5.222 -
   5.223 -        //Ipad behavior.
   5.224 -        alertController.popoverPresentationController?.sourceView = cell.languageButton
   5.225 -        alertController.popoverPresentationController?.sourceRect = cell.languageButton.bounds
   5.226 -        present(alertController, animated: true, completion: nil)
   5.227 -    }
   5.228 -}
   5.229 -
   5.230 -/// MARK: - Handshake ViewModel Delegate
   5.231 -
   5.232 -extension TrustManagementViewController: TrustManagementViewModelDelegate {
   5.233 -    
   5.234 -    @objc public func reload() {
   5.235 -        UIView.setAnimationsEnabled(false)
   5.236 -        tableView.reloadData()
   5.237 -        UIView.setAnimationsEnabled(true)
   5.238 -    }
   5.239 -}
   5.240 -
   5.241 -/// MARK: - Back button
   5.242 -
   5.243 -extension TrustManagementViewController {
   5.244 -    
   5.245 -    /// Helper method to create and set the back button in the navigation bar.
   5.246 -    private func setLeftBarButton() {
   5.247 -        let title = NSLocalizedString(" Message",
   5.248 -                                      comment: "TrustManagementView Back Button Title")
   5.249 -        let button = UIButton.backButton(with: title)
   5.250 -        let action = #selector(backButtonPressed)
   5.251 -        button.addTarget(self, action:action, for: .touchUpInside)
   5.252 -        let leftItem = UIBarButtonItem(customView: button)
   5.253 -        navigationItem.leftBarButtonItem = leftItem
   5.254 -        navigationController?.navigationBar.isTranslucent = false
   5.255 -    }
   5.256 -
   5.257 -    @objc private func backButtonPressed() {
   5.258 -        dismiss(animated: true, completion: nil)
   5.259 -    }
   5.260 -}
   5.261 -
   5.262 -/// MARK: - Notification Center
   5.263 -
   5.264 -extension TrustManagementViewController {
   5.265 -    
   5.266 -    private func registerForNotifications() {
   5.267 -        NotificationCenter.default.addObserver(self, selector: #selector(reload),
   5.268 -                                               name: UIDevice.orientationDidChangeNotification,
   5.269 -                                               object: nil)
   5.270 -    }
   5.271 -    
   5.272 -    private func unregisterNotifications() {
   5.273 -        NotificationCenter.default.removeObserver(self)
   5.274 -    }
   5.275 -}
   5.276 -
   5.277 -/// MARK: - Set trustwords
   5.278 -
   5.279 -extension TrustManagementViewController {
   5.280 -    
   5.281 -    /// Generates and sets the trustwords to the cell
   5.282 -    /// - Parameters:
   5.283 -    ///   - cell: The cell where the trustwords would be setted
   5.284 -    ///   - indexPath: The indexPath of the row to generate the trustwords.
   5.285 -    ///   - longMode: Indicates if the trustwords have to be long.
   5.286 -    private func setTrustwords(for cell: TrustManagementTableViewCell,
   5.287 -                               at indexPath: IndexPath,
   5.288 -                               longMode: Bool) {
   5.289 -        guard let vm = viewModel else {
   5.290 -            Log.shared.errorAndCrash("No VM")
   5.291 -            return
   5.292 -        }
   5.293 -        vm.generateTrustwords(forRowAt: indexPath, long: longMode) { [weak self] trustwords in
   5.294 -            guard let trustwords = trustwords else {
   5.295 -                Log.shared.debug("Trustwords are nil. The view must not be updated")
   5.296 -                return
   5.297 -            }
   5.298 -            guard let me = self else {
   5.299 -                Log.shared.error("Lost myself")
   5.300 -                return
   5.301 -            }
   5.302 -
   5.303 -            let oneSpace = " "
   5.304 -            let threeSpaces = "   "
   5.305 -            let spacedTrustwords = trustwords.replacingOccurrences(of: oneSpace, with: threeSpaces)
   5.306 -            let textToSet = longMode ? spacedTrustwords : "\(spacedTrustwords)…"
   5.307 -            if (cell.trustwordsLabel.text != textToSet) {
   5.308 -                cell.trustwordsLabel.text = textToSet
   5.309 -                me.tableView.updateSize()
   5.310 -            }
   5.311 -        }
   5.312 -    }
   5.313 -}
   5.314 -
   5.315 -/// MARK: - TrustManagementTableViewCellDelegate
   5.316 -
   5.317 -extension TrustManagementViewController: TrustManagementTableViewCellDelegate,
   5.318 -TrustManagementResetTableViewCellDelegate {
   5.319 -    func languageButtonPressed(on cell: TrustManagementTableViewCell) {
   5.320 -        showLanguagesList(for: cell)
   5.321 -    }
   5.322 -    
   5.323 -    func declineButtonPressed(on cell: TrustManagementTableViewCell) {
   5.324 -        guard let viewModel = viewModel else {
   5.325 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   5.326 -            return
   5.327 -        }
   5.328 -        if let indexPath = tableView.indexPath(for: cell) {
   5.329 -            viewModel.handleRejectHandshakePressed(at: indexPath)
   5.330 -        }
   5.331 -    }
   5.332 -    
   5.333 -    func confirmButtonPressed(on cell: TrustManagementTableViewCell) {
   5.334 -        guard let viewModel = viewModel else {
   5.335 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   5.336 -            return
   5.337 -        }
   5.338 -
   5.339 -        if let indexPath = tableView.indexPath(for: cell) {
   5.340 -            viewModel.handleConfirmHandshakePressed(at: indexPath)
   5.341 -        }
   5.342 -    }
   5.343 -    
   5.344 -    func resetButtonPressed(on cell: UITableViewCell) {
   5.345 -        guard let viewModel = viewModel else {
   5.346 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   5.347 -            return
   5.348 -        }
   5.349 -        if let indexPath = tableView.indexPath(for: cell) {
   5.350 -            viewModel.handleResetPressed(forRowAt: indexPath)
   5.351 -        }
   5.352 -    }
   5.353 -    
   5.354 -    func trustwordsLabelPressed(on cell: TrustManagementTableViewCell) {
   5.355 -        guard let viewModel = viewModel else {
   5.356 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   5.357 -            return
   5.358 -        }
   5.359 -        if let indexPath = tableView.indexPath(for: cell) {
   5.360 -            viewModel.handleToggleLongTrustwords(forRowAt: indexPath)
   5.361 -        }
   5.362 -    }
   5.363 -}
   5.364 -
   5.365 -/// MARK: - Cell configuration
   5.366 -
   5.367 -extension TrustManagementViewController {
   5.368 -    
   5.369 -    /// This method configures the layout for the provided cell.
   5.370 -    /// We use 2 different cells: one for the split view the other for iphone portrait view.
   5.371 -    /// The layout is different, so different UI structures are used.
   5.372 -    /// 
   5.373 -    /// - Parameters:
   5.374 -    ///   - identifier: As we handle two different cells, the identifier is required in order to set the layout properly.
   5.375 -    ///   - row: The row to get information to configure the cell.
   5.376 -    ///   - cell: The cell to be configured.
   5.377 -    ///   - indexPath: The indexPath of the row, to get the trustwords.
   5.378 -    private func configureTrustwords(_ identifier: String, _ row: TrustManagementViewModel.Row, _ cell: TrustManagementTableViewCell, _ indexPath: IndexPath) {
   5.379 -        ///Yellow means secure but not trusted.
   5.380 -        ///That means that's the only case must display the trustwords
   5.381 -        if identifier == onlyMasterCellIdentifier {
   5.382 -            if row.color == .yellow {
   5.383 -                setTrustwords(for: cell, at: indexPath, longMode: row.longTrustwords)
   5.384 -                cell.trustwordsStackView.isHidden = false
   5.385 -                cell.trustwordsButtonsContainer.isHidden = false
   5.386 -            } else {
   5.387 -                cell.trustwordsStackView.isHidden = true
   5.388 -                cell.trustwordsButtonsContainer.isHidden = true
   5.389 -            }
   5.390 -        } else if identifier == masterAndDetailCellIdentifier {
   5.391 -            if row.color == .yellow {
   5.392 -                setTrustwords(for: cell, at: indexPath, longMode: row.longTrustwords)
   5.393 -                cell.trustwordsLabel.isHidden = false
   5.394 -                cell.confirmButton.isHidden = false
   5.395 -                cell.declineButton.isHidden = false
   5.396 -                cell.languageButton.isHidden = false
   5.397 -            } else {
   5.398 -                cell.languageButton.isHidden = true
   5.399 -                cell.trustwordsLabel.isHidden = true
   5.400 -                cell.confirmButton.isHidden = true
   5.401 -                cell.declineButton.isHidden = true
   5.402 -            }
   5.403 -        }
   5.404 -    }
   5.405 -}
     6.1 --- a/pEpForiOS/UI/Handshake/TrustManagementViewModel.swift	Wed Mar 11 17:55:33 2020 +0100
     6.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3 @@ -1,382 +0,0 @@
     6.4 -//
     6.5 -//  TrustManagementViewModel.swift
     6.6 -//  pEp
     6.7 -//
     6.8 -//  Created by Martin Brude on 30/01/2020.
     6.9 -//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    6.10 -//
    6.11 -
    6.12 -import Foundation
    6.13 -import MessageModel
    6.14 -import PEPObjCAdapterFramework
    6.15 -
    6.16 -/// TrustManagementViewModel View Mode Delegate
    6.17 -protocol TrustManagementViewModelDelegate: class {
    6.18 -    /// Delegate method to notify that an action ends and the view must be reloaded.
    6.19 -    func reload()
    6.20 -}
    6.21 -
    6.22 -protocol TrustmanagementProtectionStateChangeDelegate: class {
    6.23 -    /// Called whenever the user toggles protection state (for the message)
    6.24 -    func protectionStateChanged(to newValue: Bool)
    6.25 -}
    6.26 -
    6.27 -extension TrustManagementViewModel {
    6.28 -    /// The item that represents the handshake partner
    6.29 -    public struct Row {
    6.30 -        /// Indicates the handshake partner's name
    6.31 -        var name: String {
    6.32 -            let name = handshakeCombination.partnerIdentity.userName
    6.33 -            let address = handshakeCombination.partnerIdentity.address
    6.34 -            return name ?? address
    6.35 -        }
    6.36 -        /// The description for the row
    6.37 -        var description: String {
    6.38 -            if forceRed {
    6.39 -                return PEPColor.red.privacyStatusDescription
    6.40 -            }
    6.41 -            return color.privacyStatusDescription
    6.42 -        }
    6.43 -        /// The privacy status name
    6.44 -        var privacyStatusName: String {
    6.45 -            if (forceRed) {
    6.46 -                return String.trustIdentityTranslation(pEpRating: .underAttack).title
    6.47 -            }
    6.48 -            let rating = handshakeCombination.partnerIdentity.pEpRating()
    6.49 -            let translations = String.trustIdentityTranslation(pEpRating: rating)
    6.50 -            return translations.title
    6.51 -        }
    6.52 -        /// The privacy status image
    6.53 -        var privacyStatusImage: UIImage? {
    6.54 -            if forceRed {
    6.55 -                return PEPColor.red.statusIconForMessage(enabled: true, withText: false)
    6.56 -            }
    6.57 -            return color.statusIconForMessage(enabled: true, withText: false)
    6.58 -        }
    6.59 -        /// The current language
    6.60 -        var currentLanguage: String
    6.61 -        /// Indicates if the trustwords are long
    6.62 -        var longTrustwords: Bool = false
    6.63 -        /// The privacy status in between the current user and the partner
    6.64 -        var privacyStatus: String?
    6.65 -        /// Status indicator
    6.66 -        var color : PEPColor {
    6.67 -            if forceRed {
    6.68 -                return PEPColor.red
    6.69 -            }
    6.70 -            return handshakeCombination.partnerIdentity.pEpColor()
    6.71 -        }
    6.72 -        var trustwords : String?
    6.73 -        //Prevents the overkill of require the trustwords when it's not necesary.
    6.74 -        fileprivate var shouldUpdateTrustwords : Bool = true
    6.75 -        fileprivate var forceRed: Bool = false
    6.76 -        /// The identity of the user to do the handshake
    6.77 -        fileprivate var handshakeCombination: TrustManagementUtil.HandshakeCombination
    6.78 -        fileprivate var fingerprint: String?
    6.79 -    }
    6.80 -}
    6.81 -
    6.82 -/// View Model to handle the TrustManagementViewModel views.
    6.83 -final class TrustManagementViewModel {
    6.84 -    weak public var delegate : TrustManagementViewModelDelegate?
    6.85 -    weak public var protectionStateChangeDelegate: TrustmanagementProtectionStateChangeDelegate?
    6.86 -    public var pEpProtected : Bool {
    6.87 -        didSet {
    6.88 -            protectionStateChangeDelegate?.protectionStateChanged(to: pEpProtected)
    6.89 -        }
    6.90 -    }
    6.91 -    var shouldShowOptionsButton: Bool = false
    6.92 -    private var message: Message
    6.93 -    private var trustManagementUtil : TrustManagementUtilProtocol?
    6.94 -    private let undoManager = UndoManager()
    6.95 -    private var actionPerformed = [String]()
    6.96 -    
    6.97 -    /// Items to be displayed in the View Controller
    6.98 -    private (set) var rows: [Row] = [Row]()
    6.99 -
   6.100 -    /// Constructor
   6.101 -    /// - Parameters:
   6.102 -    ///   - message: The message to manage the trust
   6.103 -    ///   - handshakeUtil: The tool to interact with the engine. It provides a default instance. The parameter is used for testing purposes.
   6.104 -    public init(message : Message,
   6.105 -                pEpProtectionModifyable: Bool,
   6.106 -                delegate : TrustManagementViewModelDelegate? = nil,
   6.107 -                protectionStateChangeDelegate: TrustmanagementProtectionStateChangeDelegate? = nil,
   6.108 -                trustManagementUtil: TrustManagementUtilProtocol? = TrustManagementUtil()) {
   6.109 -        self.message = message
   6.110 -        self.trustManagementUtil = trustManagementUtil
   6.111 -        self.pEpProtected = message.pEpProtected
   6.112 -        self.shouldShowOptionsButton = pEpProtectionModifyable
   6.113 -        self.delegate = delegate
   6.114 -        self.protectionStateChangeDelegate = protectionStateChangeDelegate
   6.115 -        generateRows()
   6.116 -    }
   6.117 -
   6.118 -    ///MARK - Actions
   6.119 -    
   6.120 -    /// Reject the handshake
   6.121 -    /// - Parameter indexPath: The indexPath of the item to get the user to reject the handshake
   6.122 -    public func handleRejectHandshakePressed(at indexPath: IndexPath) {
   6.123 -        guard let trustManagementViewModel = trustManagementUtil else {
   6.124 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   6.125 -            return
   6.126 -        }
   6.127 -        let actionName = NSLocalizedString("Trust Rejection", comment: "Action name to be suggested at the moment of revert")
   6.128 -        actionPerformed.append(actionName)
   6.129 -        registerUndoAction(at: indexPath)
   6.130 -        let row = rows[indexPath.row]
   6.131 -        let identity : Identity = row.handshakeCombination.partnerIdentity.safeForSession(Session.main)
   6.132 -        rows[indexPath.row].fingerprint = trustManagementViewModel.getFingerprint(for: identity)
   6.133 -        rows[indexPath.row].forceRed = true
   6.134 -        trustManagementViewModel.denyTrust(for: identity)
   6.135 -        reevaluateMessage()
   6.136 -        delegate?.reload()
   6.137 -    }
   6.138 -    
   6.139 -    /// Confirm the handshake
   6.140 -    /// - Parameter indexPath: The indexPath of the item to get the user to confirm the handshake
   6.141 -    public func handleConfirmHandshakePressed(at indexPath: IndexPath) {
   6.142 -        guard let trustManagementViewModel = trustManagementUtil else {
   6.143 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   6.144 -            return
   6.145 -        }
   6.146 -        let actionName = NSLocalizedString("Trust Confirmation", comment: "Action name to be suggested at the moment of revert")
   6.147 -        actionPerformed.append(actionName)
   6.148 -        registerUndoAction(at: indexPath)
   6.149 -        let row = rows[indexPath.row]
   6.150 -        rows[indexPath.row].forceRed = false
   6.151 -        let identity : Identity = row.handshakeCombination.partnerIdentity.safeForSession(Session.main)
   6.152 -        trustManagementViewModel.confirmTrust(for: identity)
   6.153 -        reevaluateMessage()
   6.154 -        delegate?.reload()
   6.155 -    }
   6.156 -    
   6.157 -    /// Handles the undo action.
   6.158 -    /// That means that the trust will be reseted.
   6.159 -    /// So it is not important what action in concrete was performed.
   6.160 -    /// - Parameter indexPath: The index path of the row from where the last action has been performed.
   6.161 -    @objc public func handleUndo(forRowAt indexPath: IndexPath) {
   6.162 -        guard let trustManagementViewModel = trustManagementUtil else {
   6.163 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   6.164 -            return
   6.165 -        }
   6.166 -        let row = rows[indexPath.row]
   6.167 -        rows[indexPath.row].shouldUpdateTrustwords = true
   6.168 -        rows[indexPath.row].forceRed = false
   6.169 -        trustManagementViewModel.undoMisstrustOrTrust(for: row.handshakeCombination.partnerIdentity,
   6.170 -                                                      fingerprint: row.fingerprint)
   6.171 -        reevaluateMessage()
   6.172 -    }
   6.173 -    
   6.174 -    /// Handles the redey action
   6.175 -    /// - Parameter indexPath: The indexPath of the item to get the user to undo last action.
   6.176 -    public func handleResetPressed(forRowAt indexPath: IndexPath) {
   6.177 -        guard let trustManagementViewModel = trustManagementUtil else {
   6.178 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   6.179 -            return
   6.180 -        }
   6.181 -        let row = rows[indexPath.row]
   6.182 -        rows[indexPath.row].forceRed = false
   6.183 -        trustManagementViewModel.resetTrust(for: row.handshakeCombination.partnerIdentity)
   6.184 -        reevaluateMessage()
   6.185 -        delegate?.reload()
   6.186 -    }
   6.187 -
   6.188 -    /// - returns: the available languages.
   6.189 -    public func handleChangeLanguagePressed(forRowAt indexPath : IndexPath) -> [String] {
   6.190 -        guard let trustManagementViewModel = trustManagementUtil else {
   6.191 -            Log.shared.errorAndCrash("TrustManagementViewModelDelegate is nil")
   6.192 -            return [String]()
   6.193 -        }
   6.194 -        rows[indexPath.row].shouldUpdateTrustwords = true
   6.195 -        guard let list = trustManagementViewModel.languagesList() else {
   6.196 -            Log.shared.error("The list of languages could be retrieved.")
   6.197 -            return [String]()
   6.198 -        }
   6.199 -        return list
   6.200 -    }
   6.201 -    
   6.202 -    /// Updates the selected language for that row.
   6.203 -    /// - Parameters:
   6.204 -    ///   - indexPath: The index path of the row
   6.205 -    ///   - language: The chosen language
   6.206 -    public func didSelectLanguage(forRowAt indexPath: IndexPath, language: String) {
   6.207 -        guard let trustManagementViewModelDelegate = delegate else {
   6.208 -            Log.shared.errorAndCrash("TrustManagementViewModelDelegate is nil")
   6.209 -            return
   6.210 -        }
   6.211 -        rows[indexPath.row].currentLanguage = language
   6.212 -        trustManagementViewModelDelegate.reload()
   6.213 -    }
   6.214 -    
   6.215 -    /// Toogle pEp protection status
   6.216 -    public func handleToggleProtectionPressed() {
   6.217 -        pEpProtected = !pEpProtected
   6.218 -    }
   6.219 -
   6.220 -    /// Informs if is it possible to undo an action.
   6.221 -    /// - returns: Indicates if it's possible to undo an action.
   6.222 -    public func canUndo() -> Bool {
   6.223 -        return undoManager.canUndo
   6.224 -    }
   6.225 -    
   6.226 -    /// - returns: The name of the last action performed, nil if there isn't any.
   6.227 -    public func lastActionPerformed() -> String? {
   6.228 -        return actionPerformed.last
   6.229 -    }
   6.230 -
   6.231 -    /// Method that makes the trustwords long or short (more or less trustwords in fact).
   6.232 -    /// - Parameter indexPath: The indexPath to get the row to toogle the status (long/short)
   6.233 -    public func handleToggleLongTrustwords(forRowAt indexPath: IndexPath) {
   6.234 -        guard let trustManagementViewModelDelegate = delegate else {
   6.235 -            Log.shared.errorAndCrash("TrustManagementViewModelDelegate is nil")
   6.236 -            return
   6.237 -        }
   6.238 -        rows[indexPath.row].shouldUpdateTrustwords = true
   6.239 -        rows[indexPath.row].longTrustwords.toggle()
   6.240 -        trustManagementViewModelDelegate.reload()
   6.241 -    }
   6.242 -
   6.243 -    public typealias TrustWords = String
   6.244 -
   6.245 -    /// Generate the trustwords
   6.246 -    /// - Parameters:
   6.247 -    ///   - indexPath: The indexPath of the row to toogle the status (long/short)
   6.248 -    ///   - long: Indicates if the trustwords MUST be long (more words)
   6.249 -    ///   - completion: the completion block to be executed once the trustwords are generated.
   6.250 -    /// If the trustwords passed are nil the UI must no be updated.
   6.251 -    /// If the completion block is nil, only the model in the row at the provided indexPath will be updated.
   6.252 -    public func generateTrustwords(forRowAt indexPath: IndexPath,
   6.253 -                                   long : Bool = false,
   6.254 -                                   completion: ((TrustWords?) -> Void)?)  {
   6.255 -        guard rows[indexPath.row].shouldUpdateTrustwords else {
   6.256 -            if let trustwords = rows[indexPath.row].trustwords {
   6.257 -                completion?(trustwords)
   6.258 -                return
   6.259 -            }
   6.260 -            completion?(nil)
   6.261 -            return
   6.262 -        }
   6.263 -        rows[indexPath.row].shouldUpdateTrustwords = false
   6.264 -        DispatchQueue.global(qos: .userInteractive).async { [weak self] in
   6.265 -            guard let me = self,
   6.266 -                let trustManagementViewModel = me.trustManagementUtil else {
   6.267 -                completion?(nil)
   6.268 -                Log.shared.errorAndCrash("Lost myself or the trustManagement ViewModel")
   6.269 -                return
   6.270 -            }
   6.271 -            let complete: (TrustWords?) -> Void = { trustwords in
   6.272 -                DispatchQueue.main.async {
   6.273 -                    completion?(trustwords)
   6.274 -                }
   6.275 -            }
   6.276 -            let handshakeItem = me.rows[indexPath.row]
   6.277 -            handshakeItem.handshakeCombination.ownIdentity.session.performAndWait {
   6.278 -                let selfIdentity = handshakeItem.handshakeCombination.ownIdentity
   6.279 -                let partnerIdentity = handshakeItem.handshakeCombination.partnerIdentity
   6.280 -                guard let trustwords = try? trustManagementViewModel.getTrustwords(for: selfIdentity,
   6.281 -                                                                                   and: partnerIdentity,
   6.282 -                                                                                   language: handshakeItem.currentLanguage,
   6.283 -                                                                                   long: long)
   6.284 -                    else {
   6.285 -                        Log.shared.errorAndCrash("No Trustwords")
   6.286 -                        completion?(nil)
   6.287 -                        return
   6.288 -                }
   6.289 -                me.rows[indexPath.row].trustwords = trustwords
   6.290 -                complete(trustwords)
   6.291 -            }
   6.292 -        }
   6.293 -    }
   6.294 -
   6.295 -    /// Method that reverts the last action performed by the user
   6.296 -    /// After the execution of this method there won't be any action to un-do.
   6.297 -    public func shakeMotionDidEnd() {
   6.298 -       /// Evaluate it the undo manager can undo means if it has something registerd to undo.
   6.299 -        /// If so, undo it and reload the view.
   6.300 -        if (undoManager.canUndo) {
   6.301 -            undoManager.undo()
   6.302 -            delegate?.reload()
   6.303 -            _ = actionPerformed.popLast()
   6.304 -        }
   6.305 -    }
   6.306 -
   6.307 -    ///MARK: - Private
   6.308 -
   6.309 -    /// This must be called after every trust state change. The curently processed message might
   6.310 -    /// change color.
   6.311 -    private func reevaluateMessage() {
   6.312 -        message.session.performAndWait { [weak self] in
   6.313 -            guard let me = self else {
   6.314 -                Log.shared.error("Lost myself - The message will not be reevaluated")
   6.315 -                return
   6.316 -            }
   6.317 -            RatingReEvaluator.reevaluate(message: me.message)
   6.318 -        }
   6.319 -    }
   6.320 -
   6.321 -    /// Method that generates the rows to be used by the VC
   6.322 -    private func generateRows() {
   6.323 -        guard let trustManagementViewModel = trustManagementUtil else {
   6.324 -            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
   6.325 -            return
   6.326 -        }
   6.327 -        let combinations = trustManagementViewModel.handshakeCombinations(message: message)
   6.328 -
   6.329 -        for (index, combination) in combinations.enumerated() {
   6.330 -            let backupLanguage = "en"
   6.331 -            let language =
   6.332 -            combination.partnerIdentity.language ?? Locale.current.languageCode ?? backupLanguage
   6.333 -            let row = Row(currentLanguage: language,
   6.334 -                          longTrustwords: false,
   6.335 -                          handshakeCombination: combination)
   6.336 -            rows.append(row)
   6.337 -            let indexPath = IndexPath(row: index, section: 0)
   6.338 -            generateTrustwords(forRowAt: indexPath, completion: nil)
   6.339 -        }
   6.340 -    }
   6.341 -
   6.342 -    /// Register the action to be undone
   6.343 -    /// Is not important determine what action in concret must be undone.
   6.344 -    /// The trust management util -and thus, the engine- will take care of that.
   6.345 -    /// - Parameter indexPath: The indexPath of the row which the action to undo.
   6.346 -    private func registerUndoAction(at indexPath: IndexPath) {
   6.347 -        undoManager.registerUndo(withTarget: self,
   6.348 -                                 selector: #selector(handleUndo(forRowAt:)),
   6.349 -                                 object: indexPath)
   6.350 -    }
   6.351 -}
   6.352 -
   6.353 -/// MARK: - Image 
   6.354 -
   6.355 -extension TrustManagementViewModel {
   6.356 -    
   6.357 -    /// Method that returns the user image for the current indexPath throught the callback
   6.358 -    /// - Parameters:
   6.359 -    ///   - indexPath: The index path to get the user image
   6.360 -    ///   - complete: The callback with the image
   6.361 -    public func getImage(forRowAt indexPath: IndexPath, complete: @escaping (UIImage?) -> ()) {
   6.362 -        
   6.363 -        //Check if it's cached, use it if so.
   6.364 -        let handshakeItem : Row = rows[indexPath.row]
   6.365 -        let partnerIdentity = handshakeItem.handshakeCombination.partnerIdentity
   6.366 -        let contactImageTool = IdentityImageTool()
   6.367 -        let key = IdentityImageTool.IdentityKey(identity: partnerIdentity)
   6.368 -        if let cachedContactImage = contactImageTool.cachedIdentityImage(for: key) {
   6.369 -            complete(cachedContactImage)
   6.370 -            return
   6.371 -        }
   6.372 -        
   6.373 -        //If can't find the image in the cache, creates it from the session.
   6.374 -        let session = Session()
   6.375 -        let safePartnerIdentity = partnerIdentity.safeForSession(session)
   6.376 -        DispatchQueue.global(qos: .userInteractive).async {
   6.377 -            session.performAndWait {
   6.378 -                if let contactImage = contactImageTool.identityImage(for:
   6.379 -                    IdentityImageTool.IdentityKey(identity: safePartnerIdentity)) {
   6.380 -                    complete(contactImage)
   6.381 -                }
   6.382 -            }
   6.383 -        }
   6.384 -    }
   6.385 -}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/pEpForiOS/UI/TrustManagement/CellsAndSections/TrustManagementResetTableViewCell.swift	Wed Mar 11 18:30:53 2020 +0100
     7.3 @@ -0,0 +1,35 @@
     7.4 +//
     7.5 +//  TrustManagementResetTableViewCell.swift
     7.6 +//  pEp
     7.7 +//
     7.8 +//  Created by Martin Brude on 17/02/2020.
     7.9 +//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    7.10 +//
    7.11 +
    7.12 +import UIKit
    7.13 +
    7.14 +final class TrustManagementResetTableViewCell: UITableViewCell {
    7.15 +
    7.16 +    @IBOutlet weak var partnerNameLabel: UILabel!
    7.17 +    @IBOutlet weak var partnerImageView: UIImageView!
    7.18 +    @IBOutlet weak var resetLabel: UILabel!
    7.19 +    @IBOutlet weak var resetButton: UIButton!
    7.20 +    
    7.21 +    weak var delegate : TrustManagementResetTableViewCellDelegate?
    7.22 +
    7.23 +    override func awakeFromNib() {
    7.24 +        super.awakeFromNib()
    7.25 +        
    7.26 +           //Reset Button
    7.27 +           resetButton.pEpIfyForTrust(backgroundColor: .pEpGrayBackgroundReset, textColor: .white)
    7.28 +        
    7.29 +           //Reset label
    7.30 +           resetLabel.text = NSLocalizedString("Reset all p≡p data for this comunication partner:",
    7.31 +                                               comment: "Reset all p≡p data for this comunication partner:")
    7.32 +
    7.33 +    }
    7.34 +
    7.35 +    @IBAction private func resetButtonPressed() {
    7.36 +        delegate?.resetButtonPressed(on: self)
    7.37 +    }
    7.38 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/pEpForiOS/UI/TrustManagement/CellsAndSections/TrustManagementTableViewCell.swift	Wed Mar 11 18:30:53 2020 +0100
     8.3 @@ -0,0 +1,104 @@
     8.4 +//
     8.5 +//  HandshakePartnerTableViewCell.swift
     8.6 +//  pEp
     8.7 +//
     8.8 +//  Created by Martin Brude on 07/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 +/// UITableViewCell for trust management screen
    8.15 +final class TrustManagementTableViewCell: UITableViewCell {
    8.16 + 
    8.17 +    //Content
    8.18 +    @IBOutlet weak var partnerImageView: UIImageView!
    8.19 +    @IBOutlet weak var privacyStatusImageView: UIImageView!
    8.20 +    @IBOutlet weak var partnerNameLabel: UILabel!
    8.21 +    @IBOutlet weak var privacyStatusLabel: UILabel!
    8.22 +    @IBOutlet weak var trustwordsLabel: UILabel!
    8.23 +    @IBOutlet weak var descriptionLabel: UILabel!
    8.24 +    @IBOutlet weak var languageButton: UIButton!
    8.25 +    
    8.26 +    //Only for i18n and layout
    8.27 +    @IBOutlet weak var resetLabel: UILabel!
    8.28 +    @IBOutlet weak var declineButton: UIButton!
    8.29 +    @IBOutlet weak var confirmButton: UIButton!
    8.30 +    @IBOutlet weak var resetButton: UIButton!
    8.31 +    
    8.32 +    //Hide these views in case pepColor is not yellow.
    8.33 +    @IBOutlet weak var trustwordsStackView: UIStackView!
    8.34 +    @IBOutlet weak var trustwordsButtonsContainer: UIView!
    8.35 +    
    8.36 +    weak var delegate : TrustManagementTableViewCellDelegate?
    8.37 +    
    8.38 +    override func awakeFromNib() {
    8.39 +        super.awakeFromNib()
    8.40 +        setupView()
    8.41 +    }
    8.42 +    
    8.43 +    /// Reset attributes of the cell that are not related to content.
    8.44 +    override func prepareForReuse() {
    8.45 +        super.prepareForReuse()
    8.46 +        removeGestureRecognizers()
    8.47 +    }
    8.48 +
    8.49 +    // MARK: - Actions
    8.50 +    
    8.51 +    @IBAction private func languageButtonPressed() {
    8.52 +        delegate?.languageButtonPressed(on: self)
    8.53 +    }
    8.54 +    
    8.55 +    @IBAction private func declineButtonPressed() {
    8.56 +        delegate?.declineButtonPressed(on: self)
    8.57 +    }
    8.58 +
    8.59 +    @IBAction private func confirmButtonPressed() {
    8.60 +        delegate?.confirmButtonPressed(on: self)
    8.61 +    }
    8.62 +
    8.63 +    @IBAction private func resetButtonPressed() {
    8.64 +        delegate?.resetButtonPressed(on: self)
    8.65 +    }
    8.66 +    
    8.67 +    @objc private func trustwordsLabelPressed() {
    8.68 +        delegate?.trustwordsLabelPressed(on: self)
    8.69 +    }
    8.70 +    
    8.71 +    // MARK: - Private
    8.72 +    
    8.73 +    /// Setup the view with the row data.
    8.74 +    private func setupView() {
    8.75 +        removeGestureRecognizers()
    8.76 +
    8.77 +        let gesture = UITapGestureRecognizer(target: self, action: #selector(trustwordsLabelPressed))
    8.78 +        trustwordsLabel.addGestureRecognizer(gesture)
    8.79 +    
    8.80 +        //Confirm Button
    8.81 +        let confirmTitle = NSLocalizedString("Confirm", comment: "Confirm correct trustwords/PGP fingerprint")
    8.82 +        confirmButton.setTitle(confirmTitle, for: .normal)
    8.83 +        confirmButton.pEpIfyForTrust(backgroundColor: .pEpGreen, textColor: .white)
    8.84 +        
    8.85 +        //Decline Button
    8.86 +        let declineTitle = NSLocalizedString("Decline", comment: "Incorrect trustwords/PGP fingerprint")
    8.87 +        declineButton.setTitle(declineTitle, for: .normal)
    8.88 +        declineButton.pEpIfyForTrust(backgroundColor: .pEpRed, textColor: .white)
    8.89 +
    8.90 +        //Reset Button
    8.91 +        resetButton.pEpIfyForTrust(backgroundColor: .pEpGrayBackgroundReset, textColor: .white)
    8.92 +     
    8.93 +        //Reset label
    8.94 +        resetLabel.text = NSLocalizedString("Reset all p≡p data for this comunication partner:",
    8.95 +                                            comment: "Reset all p≡p data for this comunication partner:")
    8.96 +        //Image view
    8.97 +        partnerImageView.layer.cornerRadius = 10
    8.98 +        partnerImageView.layer.masksToBounds = true
    8.99 +    }
   8.100 +
   8.101 +    private func removeGestureRecognizers() {
   8.102 +        let existingGRs = gestureRecognizers ?? []
   8.103 +        for gr in existingGRs {
   8.104 +            removeGestureRecognizer(gr)
   8.105 +        }
   8.106 +    }
   8.107 +}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/pEpForiOS/UI/TrustManagement/CellsAndSections/TrustManagementTableViewCellProtocols.swift	Wed Mar 11 18:30:53 2020 +0100
     9.3 @@ -0,0 +1,33 @@
     9.4 +//
     9.5 +//  TrustManagementTableViewCellProtocols.swift
     9.6 +//  pEp
     9.7 +//
     9.8 +//  Created by Martin Brude on 18/02/2020.
     9.9 +//  Copyright © 2020 p≡p Security S.A. All rights reserved.
    9.10 +//
    9.11 +
    9.12 +import Foundation
    9.13 +
    9.14 +/// Delegate to notify the events in the cell.
    9.15 +protocol TrustManagementResetTableViewCellDelegate: class {
    9.16 +    /// Delegate method to notify the reset button has been pressed.
    9.17 +    /// - Parameter cell: The cell where the reset button has been pressed
    9.18 +    func resetButtonPressed(on cell: UITableViewCell)
    9.19 +}
    9.20 +
    9.21 +/// Delegate to notify the events in the cell.
    9.22 +protocol TrustManagementTableViewCellDelegate: TrustManagementResetTableViewCellDelegate {
    9.23 +    
    9.24 +    /// Delegate method to notify the language button has been pressed.
    9.25 +    /// - Parameter cell: The cell where the language button has been pressed
    9.26 +    func languageButtonPressed(on cell: TrustManagementTableViewCell)
    9.27 +    /// Delegate method to notify the decline button has been pressed.
    9.28 +    /// - Parameter cell: The cell where the decline button has been pressed
    9.29 +    func declineButtonPressed(on cell: TrustManagementTableViewCell)
    9.30 +    /// Delegate method to notify the confirm button has been pressed.
    9.31 +    /// - Parameter cell: The cell where the confirm button has been pressed
    9.32 +    func confirmButtonPressed(on cell: TrustManagementTableViewCell)
    9.33 +    /// Delegate method to notify the trustwords label has been pressed.
    9.34 +    /// - Parameter cell: The cell where the trustwords label has been pressed
    9.35 +    func trustwordsLabelPressed(on cell : TrustManagementTableViewCell)
    9.36 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/pEpForiOS/UI/TrustManagement/TrustManagementViewController.swift	Wed Mar 11 18:30:53 2020 +0100
    10.3 @@ -0,0 +1,402 @@
    10.4 +//
    10.5 +//  TrustManagementViewController.swift
    10.6 +//  pEp
    10.7 +//
    10.8 +//  Created by Martin Brude on 05/02/2020.
    10.9 +//  Copyright © 2020 p≡p Security S.A. All rights reserved.
   10.10 +//
   10.11 +
   10.12 +import UIKit
   10.13 +
   10.14 +/// View Controller to handle the HandshakeView.
   10.15 +class TrustManagementViewController: BaseViewController {
   10.16 +    private let onlyMasterCellIdentifier = "TrustManagementTableViewCell_OnlyMaster"
   10.17 +    private let masterAndDetailCellIdentifier = "TrustManagementTableViewCell_Detailed"
   10.18 +    private let resetCellIdentifier = "TrustManagementTableViewResetCell"
   10.19 +    @IBOutlet weak var tableView: UITableView!
   10.20 +    @IBOutlet weak var optionsButton: UIBarButtonItem!
   10.21 +
   10.22 +    var viewModel : TrustManagementViewModel?
   10.23 +
   10.24 +    override func viewDidLoad() {
   10.25 +        super.viewDidLoad()
   10.26 +        setup()
   10.27 +        tableView.rowHeight = UITableView.automaticDimension
   10.28 +        tableView.estimatedRowHeight = 400
   10.29 +    }
   10.30 +
   10.31 +    override func viewWillAppear(_ animated: Bool) {
   10.32 +        super.viewWillAppear(animated)
   10.33 +        guard let vm = viewModel else {
   10.34 +            Log.shared.errorAndCrash("The viewModel must not be nil")
   10.35 +            return
   10.36 +        }
   10.37 +        if (!vm.shouldShowOptionsButton) {
   10.38 +            navigationItem.rightBarButtonItems?.removeAll(where: {$0 == optionsButton})
   10.39 +        } else {
   10.40 +            optionsButton.title = NSLocalizedString("Options", comment: "Options")
   10.41 +        }
   10.42 +        vm.delegate = self
   10.43 +    }
   10.44 +
   10.45 +    override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
   10.46 +        guard let vm = viewModel, vm.canUndo() && motion == .motionShake,
   10.47 +            let actionName = vm.lastActionPerformed() else { return }
   10.48 +        let title = NSLocalizedString("Undo \(actionName)", comment: "Undo trust change verification alert title")
   10.49 +        let alertController = UIAlertController.pEpAlertController(title: title,
   10.50 +                                                                   message: nil,
   10.51 +                                                                   preferredStyle: .alert)
   10.52 +        let confirmTitle = NSLocalizedString("Undo", comment: "Undo trust change verification button title")
   10.53 +        let action = UIAlertAction(title: confirmTitle, style: .default) { [weak vm] (action) in
   10.54 +            vm?.shakeMotionDidEnd()
   10.55 +        }
   10.56 +        alertController.addAction(action)
   10.57 +        
   10.58 +        //For the cancel button another action.
   10.59 +        let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel trust change to be undone")
   10.60 +        let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) { _ in
   10.61 +            alertController.dismiss(animated: true, completion: nil)
   10.62 +        }
   10.63 +        alertController.addAction(cancelAction)
   10.64 +        present(alertController, animated: true, completion: nil)
   10.65 +    }
   10.66 +
   10.67 +    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
   10.68 +        super.viewWillTransition(to: size, with: coordinator)
   10.69 +        tableView.reloadData()
   10.70 +    }
   10.71 +
   10.72 +    @IBAction private func optionsButtonPressed(_ sender: UIBarButtonItem) {
   10.73 +        presentToogleProtectionActionSheet()
   10.74 +    }
   10.75 +   
   10.76 +    deinit {
   10.77 +        unregisterNotifications()
   10.78 +    }
   10.79 +}
   10.80 +
   10.81 +// MARK: - Private
   10.82 +
   10.83 +extension TrustManagementViewController {
   10.84 +
   10.85 +    private func setup() {
   10.86 +        registerForNotifications()
   10.87 +        setLeftBarButton()
   10.88 +    }
   10.89 +}
   10.90 +
   10.91 +/// MARK: - UITableViewDataSource
   10.92 +
   10.93 +extension TrustManagementViewController : UITableViewDataSource  {
   10.94 +    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
   10.95 +        guard let numberOfRows = viewModel?.rows.count else {
   10.96 +            Log.shared.error("The viewModel must not be nil")
   10.97 +            return 0
   10.98 +        }
   10.99 +        return numberOfRows
  10.100 +    }
  10.101 +    
  10.102 +    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  10.103 +        guard let row = viewModel?.rows[indexPath.row] else {
  10.104 +            Log.shared.error("The row couldn't be dequeued")
  10.105 +            return UITableViewCell()
  10.106 +        }
  10.107 +        
  10.108 +        /// Cell for reset
  10.109 +        if row.color == .noColor,
  10.110 +            let cell = tableView.dequeueReusableCell(withIdentifier: resetCellIdentifier, for: indexPath)
  10.111 +                as? TrustManagementResetTableViewCell {
  10.112 +            cell.delegate = self
  10.113 +            cell.partnerNameLabel.text = row.name
  10.114 +            viewModel?.getImage(forRowAt: indexPath, complete: { (image) in
  10.115 +                DispatchQueue.main.async {
  10.116 +                    cell.partnerImageView.image = image
  10.117 +                }
  10.118 +            })
  10.119 +            return cell
  10.120 +        }
  10.121 +         
  10.122 +        /// Cell ´no-noColor´ context
  10.123 +        let identifier : String
  10.124 +        if UIDevice.current.orientation.isLandscape ||
  10.125 +            UIDevice.current.userInterfaceIdiom == .pad {
  10.126 +            identifier = masterAndDetailCellIdentifier
  10.127 +        } else {
  10.128 +            identifier = onlyMasterCellIdentifier
  10.129 +        }
  10.130 +
  10.131 +        guard let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
  10.132 +            as? TrustManagementTableViewCell else {
  10.133 +                Log.shared.error("The TrustManagementTableViewCell couldn't be dequeued")
  10.134 +                return UITableViewCell()
  10.135 +        }
  10.136 +        viewModel?.getImage(forRowAt: indexPath, complete: { (image) in
  10.137 +            DispatchQueue.main.async {
  10.138 +                cell.partnerImageView.image = image
  10.139 +            }
  10.140 +        })
  10.141 +        cell.privacyStatusImageView.image = row.privacyStatusImage
  10.142 +        cell.partnerNameLabel.text = row.name
  10.143 +        cell.privacyStatusLabel.text = row.privacyStatusName
  10.144 +        cell.descriptionLabel.text = row.description
  10.145 +        configureTrustwords(identifier, row, cell, indexPath)
  10.146 +        cell.delegate = self
  10.147 +        return cell
  10.148 +    }
  10.149 +}
  10.150 +
  10.151 +/// MARK: - UIAlertControllers
  10.152 +
  10.153 +extension TrustManagementViewController {
  10.154 +    
  10.155 +    /// This should only be used if the flow comes from the Compose View.
  10.156 +    private func presentToogleProtectionActionSheet() {
  10.157 +        guard let viewModel = viewModel else {
  10.158 +            Log.shared.errorAndCrash("View Model must not be nil")
  10.159 +            return
  10.160 +        }
  10.161 +        let alertController = UIAlertController.pEpAlertController(title: nil, message: nil,
  10.162 +                                                                   preferredStyle: .actionSheet)
  10.163 +        let enable = NSLocalizedString("Enable Protection", comment: "Enable Protection")
  10.164 +        let disable = NSLocalizedString("Disable Protection", comment: "Disable Protection")
  10.165 +        let toogleProtectionTitle = viewModel.pEpProtected  ? disable : enable
  10.166 +        let action = UIAlertAction(title: toogleProtectionTitle, style: .default) { (action) in
  10.167 +            viewModel.handleToggleProtectionPressed()
  10.168 +        }
  10.169 +        alertController.addAction(action)
  10.170 +        let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
  10.171 +        let cancelAction = UIAlertAction(title:cancelTitle , style: .cancel) { _ in
  10.172 +            alertController.dismiss(animated: true, completion: nil)
  10.173 +        }
  10.174 +        alertController.addAction(cancelAction)
  10.175 +        
  10.176 +        /// A broken contraint comes up, it's a known issue in iOS.
  10.177 +        /// https://github.com/lionheart/openradar-mirror/issues/21120
  10.178 +        if let buttonView = optionsButton.value(forKey: "view") as? UIView {
  10.179 +            alertController.popoverPresentationController?.sourceView = buttonView
  10.180 +            alertController.popoverPresentationController?.sourceRect = buttonView.bounds
  10.181 +        }
  10.182 +        present(alertController, animated: true, completion: nil)
  10.183 +    }
  10.184 +    
  10.185 +    /// Shows an action sheet with languages when the user taps the language button from a cell
  10.186 +    /// - Parameter cell: The cell of the language button tapped.
  10.187 +    private func showLanguagesList(for cell: TrustManagementTableViewCell) {
  10.188 +        guard let indexPath = tableView.indexPath(for: cell) else {
  10.189 +            Log.shared.error("IndexPath not found")
  10.190 +            return
  10.191 +        }
  10.192 +        let alertController = UIAlertController.pEpAlertController(title: nil,
  10.193 +                                                                   message: nil,
  10.194 +                                                                   preferredStyle: .actionSheet)
  10.195 +        guard let languages = viewModel?.handleChangeLanguagePressed(forRowAt: indexPath) else {
  10.196 +            Log.shared.error("Languages not found")
  10.197 +            return
  10.198 +        }
  10.199 +        //For every language a row in the action sheet.
  10.200 +        for language in languages {
  10.201 +            guard let languageName = NSLocale.current.localizedString(forLanguageCode: language)
  10.202 +                else {
  10.203 +                    Log.shared.debug("Language name not found")
  10.204 +                    break
  10.205 +            }
  10.206 +            let action = UIAlertAction(title: languageName, style: .default) { [weak self] (action) in
  10.207 +                guard let me = self else {
  10.208 +                    Log.shared.error("Lost myself")
  10.209 +                    return
  10.210 +                }
  10.211 +                me.viewModel?.didSelectLanguage(forRowAt: indexPath,
  10.212 +                                                language: language)
  10.213 +            }
  10.214 +            alertController.addAction(action)
  10.215 +        }
  10.216 +        
  10.217 +        //For the cancel button another action.
  10.218 +        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in
  10.219 +            alertController.dismiss(animated: true, completion: nil)
  10.220 +        }
  10.221 +        alertController.addAction(cancelAction)
  10.222 +
  10.223 +        //Ipad behavior.
  10.224 +        alertController.popoverPresentationController?.sourceView = cell.languageButton
  10.225 +        alertController.popoverPresentationController?.sourceRect = cell.languageButton.bounds
  10.226 +        present(alertController, animated: true, completion: nil)
  10.227 +    }
  10.228 +}
  10.229 +
  10.230 +/// MARK: - Handshake ViewModel Delegate
  10.231 +
  10.232 +extension TrustManagementViewController: TrustManagementViewModelDelegate {
  10.233 +    
  10.234 +    @objc public func reload() {
  10.235 +        UIView.setAnimationsEnabled(false)
  10.236 +        tableView.reloadData()
  10.237 +        UIView.setAnimationsEnabled(true)
  10.238 +    }
  10.239 +}
  10.240 +
  10.241 +/// MARK: - Back button
  10.242 +
  10.243 +extension TrustManagementViewController {
  10.244 +    
  10.245 +    /// Helper method to create and set the back button in the navigation bar.
  10.246 +    private func setLeftBarButton() {
  10.247 +        let title = NSLocalizedString(" Message",
  10.248 +                                      comment: "TrustManagementView Back Button Title")
  10.249 +        let button = UIButton.backButton(with: title)
  10.250 +        let action = #selector(backButtonPressed)
  10.251 +        button.addTarget(self, action:action, for: .touchUpInside)
  10.252 +        let leftItem = UIBarButtonItem(customView: button)
  10.253 +        navigationItem.leftBarButtonItem = leftItem
  10.254 +        navigationController?.navigationBar.isTranslucent = false
  10.255 +    }
  10.256 +
  10.257 +    @objc private func backButtonPressed() {
  10.258 +        dismiss(animated: true, completion: nil)
  10.259 +    }
  10.260 +}
  10.261 +
  10.262 +/// MARK: - Notification Center
  10.263 +
  10.264 +extension TrustManagementViewController {
  10.265 +    
  10.266 +    private func registerForNotifications() {
  10.267 +        NotificationCenter.default.addObserver(self, selector: #selector(reload),
  10.268 +                                               name: UIDevice.orientationDidChangeNotification,
  10.269 +                                               object: nil)
  10.270 +    }
  10.271 +    
  10.272 +    private func unregisterNotifications() {
  10.273 +        NotificationCenter.default.removeObserver(self)
  10.274 +    }
  10.275 +}
  10.276 +
  10.277 +/// MARK: - Set trustwords
  10.278 +
  10.279 +extension TrustManagementViewController {
  10.280 +    
  10.281 +    /// Generates and sets the trustwords to the cell
  10.282 +    /// - Parameters:
  10.283 +    ///   - cell: The cell where the trustwords would be setted
  10.284 +    ///   - indexPath: The indexPath of the row to generate the trustwords.
  10.285 +    ///   - longMode: Indicates if the trustwords have to be long.
  10.286 +    private func setTrustwords(for cell: TrustManagementTableViewCell,
  10.287 +                               at indexPath: IndexPath,
  10.288 +                               longMode: Bool) {
  10.289 +        guard let vm = viewModel else {
  10.290 +            Log.shared.errorAndCrash("No VM")
  10.291 +            return
  10.292 +        }
  10.293 +        vm.generateTrustwords(forRowAt: indexPath, long: longMode) { [weak self] trustwords in
  10.294 +            guard let trustwords = trustwords else {
  10.295 +                Log.shared.debug("Trustwords are nil. The view must not be updated")
  10.296 +                return
  10.297 +            }
  10.298 +            guard let me = self else {
  10.299 +                Log.shared.error("Lost myself")
  10.300 +                return
  10.301 +            }
  10.302 +
  10.303 +            let oneSpace = " "
  10.304 +            let threeSpaces = "   "
  10.305 +            let spacedTrustwords = trustwords.replacingOccurrences(of: oneSpace, with: threeSpaces)
  10.306 +            let textToSet = longMode ? spacedTrustwords : "\(spacedTrustwords)…"
  10.307 +            if (cell.trustwordsLabel.text != textToSet) {
  10.308 +                cell.trustwordsLabel.text = textToSet
  10.309 +                me.tableView.updateSize()
  10.310 +            }
  10.311 +        }
  10.312 +    }
  10.313 +}
  10.314 +
  10.315 +/// MARK: - TrustManagementTableViewCellDelegate
  10.316 +
  10.317 +extension TrustManagementViewController: TrustManagementTableViewCellDelegate,
  10.318 +TrustManagementResetTableViewCellDelegate {
  10.319 +    func languageButtonPressed(on cell: TrustManagementTableViewCell) {
  10.320 +        showLanguagesList(for: cell)
  10.321 +    }
  10.322 +    
  10.323 +    func declineButtonPressed(on cell: TrustManagementTableViewCell) {
  10.324 +        guard let viewModel = viewModel else {
  10.325 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  10.326 +            return
  10.327 +        }
  10.328 +        if let indexPath = tableView.indexPath(for: cell) {
  10.329 +            viewModel.handleRejectHandshakePressed(at: indexPath)
  10.330 +        }
  10.331 +    }
  10.332 +    
  10.333 +    func confirmButtonPressed(on cell: TrustManagementTableViewCell) {
  10.334 +        guard let viewModel = viewModel else {
  10.335 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  10.336 +            return
  10.337 +        }
  10.338 +
  10.339 +        if let indexPath = tableView.indexPath(for: cell) {
  10.340 +            viewModel.handleConfirmHandshakePressed(at: indexPath)
  10.341 +        }
  10.342 +    }
  10.343 +    
  10.344 +    func resetButtonPressed(on cell: UITableViewCell) {
  10.345 +        guard let viewModel = viewModel else {
  10.346 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  10.347 +            return
  10.348 +        }
  10.349 +        if let indexPath = tableView.indexPath(for: cell) {
  10.350 +            viewModel.handleResetPressed(forRowAt: indexPath)
  10.351 +        }
  10.352 +    }
  10.353 +    
  10.354 +    func trustwordsLabelPressed(on cell: TrustManagementTableViewCell) {
  10.355 +        guard let viewModel = viewModel else {
  10.356 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  10.357 +            return
  10.358 +        }
  10.359 +        if let indexPath = tableView.indexPath(for: cell) {
  10.360 +            viewModel.handleToggleLongTrustwords(forRowAt: indexPath)
  10.361 +        }
  10.362 +    }
  10.363 +}
  10.364 +
  10.365 +/// MARK: - Cell configuration
  10.366 +
  10.367 +extension TrustManagementViewController {
  10.368 +    
  10.369 +    /// This method configures the layout for the provided cell.
  10.370 +    /// We use 2 different cells: one for the split view the other for iphone portrait view.
  10.371 +    /// The layout is different, so different UI structures are used.
  10.372 +    /// 
  10.373 +    /// - Parameters:
  10.374 +    ///   - identifier: As we handle two different cells, the identifier is required in order to set the layout properly.
  10.375 +    ///   - row: The row to get information to configure the cell.
  10.376 +    ///   - cell: The cell to be configured.
  10.377 +    ///   - indexPath: The indexPath of the row, to get the trustwords.
  10.378 +    private func configureTrustwords(_ identifier: String, _ row: TrustManagementViewModel.Row, _ cell: TrustManagementTableViewCell, _ indexPath: IndexPath) {
  10.379 +        ///Yellow means secure but not trusted.
  10.380 +        ///That means that's the only case must display the trustwords
  10.381 +        if identifier == onlyMasterCellIdentifier {
  10.382 +            if row.color == .yellow {
  10.383 +                setTrustwords(for: cell, at: indexPath, longMode: row.longTrustwords)
  10.384 +                cell.trustwordsStackView.isHidden = false
  10.385 +                cell.trustwordsButtonsContainer.isHidden = false
  10.386 +            } else {
  10.387 +                cell.trustwordsStackView.isHidden = true
  10.388 +                cell.trustwordsButtonsContainer.isHidden = true
  10.389 +            }
  10.390 +        } else if identifier == masterAndDetailCellIdentifier {
  10.391 +            if row.color == .yellow {
  10.392 +                setTrustwords(for: cell, at: indexPath, longMode: row.longTrustwords)
  10.393 +                cell.trustwordsLabel.isHidden = false
  10.394 +                cell.confirmButton.isHidden = false
  10.395 +                cell.declineButton.isHidden = false
  10.396 +                cell.languageButton.isHidden = false
  10.397 +            } else {
  10.398 +                cell.languageButton.isHidden = true
  10.399 +                cell.trustwordsLabel.isHidden = true
  10.400 +                cell.confirmButton.isHidden = true
  10.401 +                cell.declineButton.isHidden = true
  10.402 +            }
  10.403 +        }
  10.404 +    }
  10.405 +}
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/pEpForiOS/UI/TrustManagement/TrustManagementViewModel.swift	Wed Mar 11 18:30:53 2020 +0100
    11.3 @@ -0,0 +1,382 @@
    11.4 +//
    11.5 +//  TrustManagementViewModel.swift
    11.6 +//  pEp
    11.7 +//
    11.8 +//  Created by Martin Brude on 30/01/2020.
    11.9 +//  Copyright © 2020 p≡p Security S.A. All rights reserved.
   11.10 +//
   11.11 +
   11.12 +import Foundation
   11.13 +import MessageModel
   11.14 +import PEPObjCAdapterFramework
   11.15 +
   11.16 +/// TrustManagementViewModel View Mode Delegate
   11.17 +protocol TrustManagementViewModelDelegate: class {
   11.18 +    /// Delegate method to notify that an action ends and the view must be reloaded.
   11.19 +    func reload()
   11.20 +}
   11.21 +
   11.22 +protocol TrustmanagementProtectionStateChangeDelegate: class {
   11.23 +    /// Called whenever the user toggles protection state (for the message)
   11.24 +    func protectionStateChanged(to newValue: Bool)
   11.25 +}
   11.26 +
   11.27 +extension TrustManagementViewModel {
   11.28 +    /// The item that represents the handshake partner
   11.29 +    public struct Row {
   11.30 +        /// Indicates the handshake partner's name
   11.31 +        var name: String {
   11.32 +            let name = handshakeCombination.partnerIdentity.userName
   11.33 +            let address = handshakeCombination.partnerIdentity.address
   11.34 +            return name ?? address
   11.35 +        }
   11.36 +        /// The description for the row
   11.37 +        var description: String {
   11.38 +            if forceRed {
   11.39 +                return PEPColor.red.privacyStatusDescription
   11.40 +            }
   11.41 +            return color.privacyStatusDescription
   11.42 +        }
   11.43 +        /// The privacy status name
   11.44 +        var privacyStatusName: String {
   11.45 +            if (forceRed) {
   11.46 +                return String.trustIdentityTranslation(pEpRating: .underAttack).title
   11.47 +            }
   11.48 +            let rating = handshakeCombination.partnerIdentity.pEpRating()
   11.49 +            let translations = String.trustIdentityTranslation(pEpRating: rating)
   11.50 +            return translations.title
   11.51 +        }
   11.52 +        /// The privacy status image
   11.53 +        var privacyStatusImage: UIImage? {
   11.54 +            if forceRed {
   11.55 +                return PEPColor.red.statusIconForMessage(enabled: true, withText: false)
   11.56 +            }
   11.57 +            return color.statusIconForMessage(enabled: true, withText: false)
   11.58 +        }
   11.59 +        /// The current language
   11.60 +        var currentLanguage: String
   11.61 +        /// Indicates if the trustwords are long
   11.62 +        var longTrustwords: Bool = false
   11.63 +        /// The privacy status in between the current user and the partner
   11.64 +        var privacyStatus: String?
   11.65 +        /// Status indicator
   11.66 +        var color : PEPColor {
   11.67 +            if forceRed {
   11.68 +                return PEPColor.red
   11.69 +            }
   11.70 +            return handshakeCombination.partnerIdentity.pEpColor()
   11.71 +        }
   11.72 +        var trustwords : String?
   11.73 +        //Prevents the overkill of require the trustwords when it's not necesary.
   11.74 +        fileprivate var shouldUpdateTrustwords : Bool = true
   11.75 +        fileprivate var forceRed: Bool = false
   11.76 +        /// The identity of the user to do the handshake
   11.77 +        fileprivate var handshakeCombination: TrustManagementUtil.HandshakeCombination
   11.78 +        fileprivate var fingerprint: String?
   11.79 +    }
   11.80 +}
   11.81 +
   11.82 +/// View Model to handle the TrustManagementViewModel views.
   11.83 +final class TrustManagementViewModel {
   11.84 +    weak public var delegate : TrustManagementViewModelDelegate?
   11.85 +    weak public var protectionStateChangeDelegate: TrustmanagementProtectionStateChangeDelegate?
   11.86 +    public var pEpProtected : Bool {
   11.87 +        didSet {
   11.88 +            protectionStateChangeDelegate?.protectionStateChanged(to: pEpProtected)
   11.89 +        }
   11.90 +    }
   11.91 +    var shouldShowOptionsButton: Bool = false
   11.92 +    private var message: Message
   11.93 +    private var trustManagementUtil : TrustManagementUtilProtocol?
   11.94 +    private let undoManager = UndoManager()
   11.95 +    private var actionPerformed = [String]()
   11.96 +    
   11.97 +    /// Items to be displayed in the View Controller
   11.98 +    private (set) var rows: [Row] = [Row]()
   11.99 +
  11.100 +    /// Constructor
  11.101 +    /// - Parameters:
  11.102 +    ///   - message: The message to manage the trust
  11.103 +    ///   - handshakeUtil: The tool to interact with the engine. It provides a default instance. The parameter is used for testing purposes.
  11.104 +    public init(message : Message,
  11.105 +                pEpProtectionModifyable: Bool,
  11.106 +                delegate : TrustManagementViewModelDelegate? = nil,
  11.107 +                protectionStateChangeDelegate: TrustmanagementProtectionStateChangeDelegate? = nil,
  11.108 +                trustManagementUtil: TrustManagementUtilProtocol? = TrustManagementUtil()) {
  11.109 +        self.message = message
  11.110 +        self.trustManagementUtil = trustManagementUtil
  11.111 +        self.pEpProtected = message.pEpProtected
  11.112 +        self.shouldShowOptionsButton = pEpProtectionModifyable
  11.113 +        self.delegate = delegate
  11.114 +        self.protectionStateChangeDelegate = protectionStateChangeDelegate
  11.115 +        generateRows()
  11.116 +    }
  11.117 +
  11.118 +    ///MARK - Actions
  11.119 +    
  11.120 +    /// Reject the handshake
  11.121 +    /// - Parameter indexPath: The indexPath of the item to get the user to reject the handshake
  11.122 +    public func handleRejectHandshakePressed(at indexPath: IndexPath) {
  11.123 +        guard let trustManagementViewModel = trustManagementUtil else {
  11.124 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  11.125 +            return
  11.126 +        }
  11.127 +        let actionName = NSLocalizedString("Trust Rejection", comment: "Action name to be suggested at the moment of revert")
  11.128 +        actionPerformed.append(actionName)
  11.129 +        registerUndoAction(at: indexPath)
  11.130 +        let row = rows[indexPath.row]
  11.131 +        let identity : Identity = row.handshakeCombination.partnerIdentity.safeForSession(Session.main)
  11.132 +        rows[indexPath.row].fingerprint = trustManagementViewModel.getFingerprint(for: identity)
  11.133 +        rows[indexPath.row].forceRed = true
  11.134 +        trustManagementViewModel.denyTrust(for: identity)
  11.135 +        reevaluateMessage()
  11.136 +        delegate?.reload()
  11.137 +    }
  11.138 +    
  11.139 +    /// Confirm the handshake
  11.140 +    /// - Parameter indexPath: The indexPath of the item to get the user to confirm the handshake
  11.141 +    public func handleConfirmHandshakePressed(at indexPath: IndexPath) {
  11.142 +        guard let trustManagementViewModel = trustManagementUtil else {
  11.143 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  11.144 +            return
  11.145 +        }
  11.146 +        let actionName = NSLocalizedString("Trust Confirmation", comment: "Action name to be suggested at the moment of revert")
  11.147 +        actionPerformed.append(actionName)
  11.148 +        registerUndoAction(at: indexPath)
  11.149 +        let row = rows[indexPath.row]
  11.150 +        rows[indexPath.row].forceRed = false
  11.151 +        let identity : Identity = row.handshakeCombination.partnerIdentity.safeForSession(Session.main)
  11.152 +        trustManagementViewModel.confirmTrust(for: identity)
  11.153 +        reevaluateMessage()
  11.154 +        delegate?.reload()
  11.155 +    }
  11.156 +    
  11.157 +    /// Handles the undo action.
  11.158 +    /// That means that the trust will be reseted.
  11.159 +    /// So it is not important what action in concrete was performed.
  11.160 +    /// - Parameter indexPath: The index path of the row from where the last action has been performed.
  11.161 +    @objc public func handleUndo(forRowAt indexPath: IndexPath) {
  11.162 +        guard let trustManagementViewModel = trustManagementUtil else {
  11.163 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  11.164 +            return
  11.165 +        }
  11.166 +        let row = rows[indexPath.row]
  11.167 +        rows[indexPath.row].shouldUpdateTrustwords = true
  11.168 +        rows[indexPath.row].forceRed = false
  11.169 +        trustManagementViewModel.undoMisstrustOrTrust(for: row.handshakeCombination.partnerIdentity,
  11.170 +                                                      fingerprint: row.fingerprint)
  11.171 +        reevaluateMessage()
  11.172 +    }
  11.173 +    
  11.174 +    /// Handles the redey action
  11.175 +    /// - Parameter indexPath: The indexPath of the item to get the user to undo last action.
  11.176 +    public func handleResetPressed(forRowAt indexPath: IndexPath) {
  11.177 +        guard let trustManagementViewModel = trustManagementUtil else {
  11.178 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  11.179 +            return
  11.180 +        }
  11.181 +        let row = rows[indexPath.row]
  11.182 +        rows[indexPath.row].forceRed = false
  11.183 +        trustManagementViewModel.resetTrust(for: row.handshakeCombination.partnerIdentity)
  11.184 +        reevaluateMessage()
  11.185 +        delegate?.reload()
  11.186 +    }
  11.187 +
  11.188 +    /// - returns: the available languages.
  11.189 +    public func handleChangeLanguagePressed(forRowAt indexPath : IndexPath) -> [String] {
  11.190 +        guard let trustManagementViewModel = trustManagementUtil else {
  11.191 +            Log.shared.errorAndCrash("TrustManagementViewModelDelegate is nil")
  11.192 +            return [String]()
  11.193 +        }
  11.194 +        rows[indexPath.row].shouldUpdateTrustwords = true
  11.195 +        guard let list = trustManagementViewModel.languagesList() else {
  11.196 +            Log.shared.error("The list of languages could be retrieved.")
  11.197 +            return [String]()
  11.198 +        }
  11.199 +        return list
  11.200 +    }
  11.201 +    
  11.202 +    /// Updates the selected language for that row.
  11.203 +    /// - Parameters:
  11.204 +    ///   - indexPath: The index path of the row
  11.205 +    ///   - language: The chosen language
  11.206 +    public func didSelectLanguage(forRowAt indexPath: IndexPath, language: String) {
  11.207 +        guard let trustManagementViewModelDelegate = delegate else {
  11.208 +            Log.shared.errorAndCrash("TrustManagementViewModelDelegate is nil")
  11.209 +            return
  11.210 +        }
  11.211 +        rows[indexPath.row].currentLanguage = language
  11.212 +        trustManagementViewModelDelegate.reload()
  11.213 +    }
  11.214 +    
  11.215 +    /// Toogle pEp protection status
  11.216 +    public func handleToggleProtectionPressed() {
  11.217 +        pEpProtected = !pEpProtected
  11.218 +    }
  11.219 +
  11.220 +    /// Informs if is it possible to undo an action.
  11.221 +    /// - returns: Indicates if it's possible to undo an action.
  11.222 +    public func canUndo() -> Bool {
  11.223 +        return undoManager.canUndo
  11.224 +    }
  11.225 +    
  11.226 +    /// - returns: The name of the last action performed, nil if there isn't any.
  11.227 +    public func lastActionPerformed() -> String? {
  11.228 +        return actionPerformed.last
  11.229 +    }
  11.230 +
  11.231 +    /// Method that makes the trustwords long or short (more or less trustwords in fact).
  11.232 +    /// - Parameter indexPath: The indexPath to get the row to toogle the status (long/short)
  11.233 +    public func handleToggleLongTrustwords(forRowAt indexPath: IndexPath) {
  11.234 +        guard let trustManagementViewModelDelegate = delegate else {
  11.235 +            Log.shared.errorAndCrash("TrustManagementViewModelDelegate is nil")
  11.236 +            return
  11.237 +        }
  11.238 +        rows[indexPath.row].shouldUpdateTrustwords = true
  11.239 +        rows[indexPath.row].longTrustwords.toggle()
  11.240 +        trustManagementViewModelDelegate.reload()
  11.241 +    }
  11.242 +
  11.243 +    public typealias TrustWords = String
  11.244 +
  11.245 +    /// Generate the trustwords
  11.246 +    /// - Parameters:
  11.247 +    ///   - indexPath: The indexPath of the row to toogle the status (long/short)
  11.248 +    ///   - long: Indicates if the trustwords MUST be long (more words)
  11.249 +    ///   - completion: the completion block to be executed once the trustwords are generated.
  11.250 +    /// If the trustwords passed are nil the UI must no be updated.
  11.251 +    /// If the completion block is nil, only the model in the row at the provided indexPath will be updated.
  11.252 +    public func generateTrustwords(forRowAt indexPath: IndexPath,
  11.253 +                                   long : Bool = false,
  11.254 +                                   completion: ((TrustWords?) -> Void)?)  {
  11.255 +        guard rows[indexPath.row].shouldUpdateTrustwords else {
  11.256 +            if let trustwords = rows[indexPath.row].trustwords {
  11.257 +                completion?(trustwords)
  11.258 +                return
  11.259 +            }
  11.260 +            completion?(nil)
  11.261 +            return
  11.262 +        }
  11.263 +        rows[indexPath.row].shouldUpdateTrustwords = false
  11.264 +        DispatchQueue.global(qos: .userInteractive).async { [weak self] in
  11.265 +            guard let me = self,
  11.266 +                let trustManagementViewModel = me.trustManagementUtil else {
  11.267 +                completion?(nil)
  11.268 +                Log.shared.errorAndCrash("Lost myself or the trustManagement ViewModel")
  11.269 +                return
  11.270 +            }
  11.271 +            let complete: (TrustWords?) -> Void = { trustwords in
  11.272 +                DispatchQueue.main.async {
  11.273 +                    completion?(trustwords)
  11.274 +                }
  11.275 +            }
  11.276 +            let handshakeItem = me.rows[indexPath.row]
  11.277 +            handshakeItem.handshakeCombination.ownIdentity.session.performAndWait {
  11.278 +                let selfIdentity = handshakeItem.handshakeCombination.ownIdentity
  11.279 +                let partnerIdentity = handshakeItem.handshakeCombination.partnerIdentity
  11.280 +                guard let trustwords = try? trustManagementViewModel.getTrustwords(for: selfIdentity,
  11.281 +                                                                                   and: partnerIdentity,
  11.282 +                                                                                   language: handshakeItem.currentLanguage,
  11.283 +                                                                                   long: long)
  11.284 +                    else {
  11.285 +                        Log.shared.errorAndCrash("No Trustwords")
  11.286 +                        completion?(nil)
  11.287 +                        return
  11.288 +                }
  11.289 +                me.rows[indexPath.row].trustwords = trustwords
  11.290 +                complete(trustwords)
  11.291 +            }
  11.292 +        }
  11.293 +    }
  11.294 +
  11.295 +    /// Method that reverts the last action performed by the user
  11.296 +    /// After the execution of this method there won't be any action to un-do.
  11.297 +    public func shakeMotionDidEnd() {
  11.298 +       /// Evaluate it the undo manager can undo means if it has something registerd to undo.
  11.299 +        /// If so, undo it and reload the view.
  11.300 +        if (undoManager.canUndo) {
  11.301 +            undoManager.undo()
  11.302 +            delegate?.reload()
  11.303 +            _ = actionPerformed.popLast()
  11.304 +        }
  11.305 +    }
  11.306 +
  11.307 +    ///MARK: - Private
  11.308 +
  11.309 +    /// This must be called after every trust state change. The curently processed message might
  11.310 +    /// change color.
  11.311 +    private func reevaluateMessage() {
  11.312 +        message.session.performAndWait { [weak self] in
  11.313 +            guard let me = self else {
  11.314 +                Log.shared.error("Lost myself - The message will not be reevaluated")
  11.315 +                return
  11.316 +            }
  11.317 +            RatingReEvaluator.reevaluate(message: me.message)
  11.318 +        }
  11.319 +    }
  11.320 +
  11.321 +    /// Method that generates the rows to be used by the VC
  11.322 +    private func generateRows() {
  11.323 +        guard let trustManagementViewModel = trustManagementUtil else {
  11.324 +            Log.shared.errorAndCrash("TrustManagementViewModel is nil")
  11.325 +            return
  11.326 +        }
  11.327 +        let combinations = trustManagementViewModel.handshakeCombinations(message: message)
  11.328 +
  11.329 +        for (index, combination) in combinations.enumerated() {
  11.330 +            let backupLanguage = "en"
  11.331 +            let language =
  11.332 +            combination.partnerIdentity.language ?? Locale.current.languageCode ?? backupLanguage
  11.333 +            let row = Row(currentLanguage: language,
  11.334 +                          longTrustwords: false,
  11.335 +                          handshakeCombination: combination)
  11.336 +            rows.append(row)
  11.337 +            let indexPath = IndexPath(row: index, section: 0)
  11.338 +            generateTrustwords(forRowAt: indexPath, completion: nil)
  11.339 +        }
  11.340 +    }
  11.341 +
  11.342 +    /// Register the action to be undone
  11.343 +    /// Is not important determine what action in concret must be undone.
  11.344 +    /// The trust management util -and thus, the engine- will take care of that.
  11.345 +    /// - Parameter indexPath: The indexPath of the row which the action to undo.
  11.346 +    private func registerUndoAction(at indexPath: IndexPath) {
  11.347 +        undoManager.registerUndo(withTarget: self,
  11.348 +                                 selector: #selector(handleUndo(forRowAt:)),
  11.349 +                                 object: indexPath)
  11.350 +    }
  11.351 +}
  11.352 +
  11.353 +/// MARK: - Image 
  11.354 +
  11.355 +extension TrustManagementViewModel {
  11.356 +    
  11.357 +    /// Method that returns the user image for the current indexPath throught the callback
  11.358 +    /// - Parameters:
  11.359 +    ///   - indexPath: The index path to get the user image
  11.360 +    ///   - complete: The callback with the image
  11.361 +    public func getImage(forRowAt indexPath: IndexPath, complete: @escaping (UIImage?) -> ()) {
  11.362 +        
  11.363 +        //Check if it's cached, use it if so.
  11.364 +        let handshakeItem : Row = rows[indexPath.row]
  11.365 +        let partnerIdentity = handshakeItem.handshakeCombination.partnerIdentity
  11.366 +        let contactImageTool = IdentityImageTool()
  11.367 +        let key = IdentityImageTool.IdentityKey(identity: partnerIdentity)
  11.368 +        if let cachedContactImage = contactImageTool.cachedIdentityImage(for: key) {
  11.369 +            complete(cachedContactImage)
  11.370 +            return
  11.371 +        }
  11.372 +        
  11.373 +        //If can't find the image in the cache, creates it from the session.
  11.374 +        let session = Session()
  11.375 +        let safePartnerIdentity = partnerIdentity.safeForSession(session)
  11.376 +        DispatchQueue.global(qos: .userInteractive).async {
  11.377 +            session.performAndWait {
  11.378 +                if let contactImage = contactImageTool.identityImage(for:
  11.379 +                    IdentityImageTool.IdentityKey(identity: safePartnerIdentity)) {
  11.380 +                    complete(contactImage)
  11.381 +                }
  11.382 +            }
  11.383 +        }
  11.384 +    }
  11.385 +}