pEpForiOS/UI/EmailDisplay/EmailViewController.swift
author Dirk Zimmermann <dirk@pep-project.org>
Thu, 01 Feb 2018 11:29:01 +0100
changeset 3866 b5901734830a
parent 3727 297ca261801c
child 3912 905dbc7a3e4e
permissions -rw-r--r--
IOS-915 logging to pinpoint the problem
     1 //
     2 //  EmailViewController.swift
     3 //  pEpForiOS
     4 //
     5 //  Created by Dirk Zimmermann on 31/05/16.
     6 //  Copyright © 2016 p≡p Security S.A. All rights reserved.
     7 //
     8 
     9 import Foundation
    10 import UIKit
    11 
    12 import MessageModel
    13 
    14 class EmailViewController: BaseTableViewController {
    15     @IBOutlet var handShakeButton: UIBarButtonItem!
    16     @IBOutlet var flagButton: UIBarButtonItem!
    17     @IBOutlet var previousMessage: UIBarButtonItem!
    18     @IBOutlet var nextMessage: UIBarButtonItem!
    19 
    20     var message: Message?
    21 
    22     var partnerIdentity: Identity?
    23     var tableData: ComposeDataSource?
    24     var folderShow : Folder?
    25     var messageId = 0
    26     var otherCellsHeight: CGFloat = 0.0
    27     var ratingReEvaluator: RatingReEvaluator?
    28 
    29     lazy var backgroundQueue = OperationQueue()
    30     lazy var documentInteractionController = UIDocumentInteractionController()
    31 
    32     override func viewDidLoad() {
    33         super.viewDidLoad()
    34         guard let m = message else{
    35             Log.shared.errorAndCrash(component: #function, errorString: "no message to show")
    36             return
    37         }
    38 
    39         loadDatasource("MessageData")
    40 
    41         tableView.estimatedRowHeight = 72.0
    42         tableView.rowHeight = UITableViewAutomaticDimension
    43         tableView.setNeedsLayout()
    44         tableView.layoutIfNeeded()
    45 
    46         self.title = m.shortMessage
    47         saveTitleView()
    48     }
    49 
    50     @IBAction func next(_ sender: Any) {
    51         messageId += 1
    52         if let m = folderShow?.messageAt(index: messageId) {
    53             message = m
    54         }
    55         //message =
    56         self.tableView.reloadData()
    57         configureView()
    58 
    59     }
    60 
    61     @IBAction func previous(_ sender: Any) {
    62         messageId -= 1
    63         if let m = folderShow?.messageAt(index: messageId) {
    64             message = m
    65         }
    66         self.tableView.reloadData()
    67         configureView()
    68     }
    69 
    70     override func viewWillAppear(_ animated: Bool) {
    71         super.viewWillAppear(animated)
    72         configureView()
    73     }
    74 
    75     override func viewWillDisappear(_ animated: Bool) {
    76         super.viewWillDisappear(animated)
    77         setNoColor()
    78     }
    79 
    80     func configureView() {
    81         tableData?.filterRows(message: message)
    82 
    83         recoveryInitialTitle()
    84 
    85         if messageId <= 0 {
    86             self.previousMessage.isEnabled = false
    87         } else {
    88             self.previousMessage.isEnabled = true
    89         }
    90 
    91         DispatchQueue.main.async {
    92             self.checkMessageReEvaluation()
    93             self.showPepRating()
    94 
    95             self.message?.markAsSeen()
    96 
    97             if let total = self.folderShow?.messageCount(), self.messageId >= total - 1 {
    98                 self.nextMessage.isEnabled = false
    99             } else {
   100                 self.nextMessage.isEnabled = true
   101             }
   102         }
   103         updateFlaggedStatus()
   104     }
   105 
   106     func updateFlaggedStatus() {
   107         if message?.imapFlags?.flagged ?? false {
   108             flagButton.image = UIImage.init(named: "icon-flagged")
   109         } else {
   110             flagButton.image = UIImage.init(named: "icon-unflagged")
   111         }
   112     }
   113 
   114     func checkMessageReEvaluation() {
   115         if let m = message, ratingReEvaluator?.message != m {
   116             ratingReEvaluator = RatingReEvaluator(parentName: #function, message: m)
   117             ratingReEvaluator?.delegate = self
   118         }
   119     }
   120 
   121     func showPepRating() {
   122         let session = PEPSession()
   123         let _ = showPepRating(pEpRating: message?.pEpRating(session: session))
   124         var allOwnKeysGenerated = true
   125         var atLeastOneHandshakableIdentityFound = false
   126         if let m = message {
   127             for id in m.allIdentities {
   128                 if id.isMySelf {
   129                     // if we encounter an own identity, make sure it already has a key
   130                     if id.fingerPrint(session: session) == nil {
   131                         allOwnKeysGenerated = false
   132                         break
   133                     }
   134                 } else {
   135                     if id.canHandshakeOn(session: session) {
   136                         atLeastOneHandshakableIdentityFound = true
   137                         break
   138                     }
   139                 }
   140             }
   141         }
   142         handShakeButton.isEnabled = allOwnKeysGenerated && atLeastOneHandshakableIdentityFound
   143     }
   144 
   145     fileprivate final func loadDatasource(_ file: String) {
   146         if let path = Bundle.main.path(forResource: file, ofType: "plist") {
   147             if let dict = NSDictionary(contentsOfFile: path) as? [String: Any] {
   148                 tableData = ComposeDataSource(with: dict["Rows"] as! [[String: Any]])
   149             }
   150         }
   151     }
   152 
   153     // MARK: - IBActions
   154 
   155     @IBAction func pressReply(_ sender: UIBarButtonItem) {
   156         let alertViewWithoutTitle = UIAlertController()
   157         alertViewWithoutTitle.view.tintColor = .pEpGreen
   158 
   159         if let popoverPresentationController = alertViewWithoutTitle.popoverPresentationController {
   160             popoverPresentationController.sourceView = view
   161         }
   162 
   163         let alertActionReply = UIAlertAction(
   164             title: NSLocalizedString("Reply", comment: "Message actions"),
   165             style: .default) { (action) in
   166                 self.performSegue(withIdentifier: .segueReplyFrom , sender: self)
   167         }
   168         alertViewWithoutTitle.addAction(alertActionReply)
   169 
   170         let alertActionReplyAll = UIAlertAction(
   171             title: NSLocalizedString("Reply All", comment: "Message actions"),
   172             style: .default) { (action) in
   173                 self.performSegue(withIdentifier: .segueReplyAllForm , sender: self)
   174         }
   175         alertViewWithoutTitle.addAction(alertActionReplyAll)
   176 
   177         let alertActionForward = UIAlertAction(
   178             title: NSLocalizedString("Forward", comment: "Message actions"),
   179             style: .default) { (action) in
   180                 self.performSegue(withIdentifier: .segueForward , sender: self)
   181         }
   182         alertViewWithoutTitle.addAction(alertActionForward)
   183 
   184         let cancelAction = UIAlertAction(
   185             title: NSLocalizedString("Cancel", comment: "Message actions"),
   186             style: .cancel) { (action) in }
   187         alertViewWithoutTitle.addAction(cancelAction)
   188 
   189         present(alertViewWithoutTitle, animated: true, completion: nil)
   190     }
   191 
   192     @IBAction func flagButtonTapped(_ sender: UIBarButtonItem) {
   193         if (message?.imapFlags?.flagged == true) {
   194             message?.imapFlags?.flagged = false
   195         } else {
   196             message?.imapFlags?.flagged = true
   197         }
   198         message?.save()
   199 
   200         updateFlaggedStatus()
   201     }
   202 
   203     @IBAction func deleteButtonTapped(_ sender: UIBarButtonItem) {
   204         message?.imapDelete() // mark for deletion/trash
   205         _ = navigationController?.popViewController(animated: true)
   206     }
   207 
   208     /**
   209      For the unwind segue from the trustwords controller, when the user choses "trusted".
   210      */
   211     @IBAction func segueUnwindTrusted(segue: UIStoryboardSegue) {
   212         if let p = partnerIdentity {
   213             let session = PEPSession()
   214             PEPUtil.trust(identity: p, session: session)
   215             decryptAgain()
   216         }
   217     }
   218 
   219     /**
   220      For the unwind segue from the trustwords controller, when the user choses "untrusted".
   221      */
   222     @IBAction func segueUnwindUnTrusted(segue: UIStoryboardSegue) {
   223         if let p = partnerIdentity {
   224             let session = PEPSession()
   225             PEPUtil.mistrust(identity: p, session: session)
   226             decryptAgain()
   227         }
   228     }
   229 
   230     func decryptAgain() {
   231         ratingReEvaluator?.reevaluateRating()
   232     }
   233 }
   234 
   235 // MARK: UITableViewDataSource
   236 
   237 extension EmailViewController {
   238     override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
   239         return tableData?.numberOfRows() ?? 0
   240     }
   241 
   242     override func tableView(
   243         _ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   244         guard
   245             let row = tableData?.getRow(at: indexPath.row),
   246             let cell = tableView.dequeueReusableCell(
   247                 withIdentifier: row.identifier,
   248                 for: indexPath) as? MessageCell,
   249             let m = message else {
   250                     return UITableViewCell()
   251         }
   252         cell.updateCell(model: row, message: m, indexPath: indexPath)
   253         cell.delegate = self
   254         return cell
   255     }
   256 }
   257 
   258 // MARK: - MessageContentCellDelegate
   259 
   260 extension EmailViewController: MessageContentCellDelegate {
   261     func didUpdate(cell: MessageCell, height: CGFloat) {
   262         tableView.updateSize()
   263     }
   264 }
   265 
   266 // MARK: - SegueHandlerType
   267 
   268 extension EmailViewController: SegueHandlerType {
   269     enum SegueIdentifier: String {
   270         case segueReplyFrom
   271         case segueReplyAllForm
   272         case segueForward
   273         case segueHandshake
   274         case segueCompose
   275         case noSegue
   276     }
   277 
   278     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
   279         let theId = segueIdentifier(for: segue)
   280         switch theId {
   281         case .segueReplyFrom, .segueReplyAllForm, .segueForward, .segueCompose:
   282             guard  let nav = segue.destination as? UINavigationController,
   283                 let destination = nav.topViewController as? ComposeTableViewController else {
   284                     Log.shared.errorAndCrash(component: #function, errorString: "No DVC?")
   285                     break
   286             }
   287 
   288             destination.appConfig = appConfig
   289 
   290             if theId == .segueReplyFrom {
   291                 destination.composeMode = .replyFrom
   292                 destination.originalMessage = message
   293             } else if theId == .segueReplyAllForm {
   294                 destination.composeMode = .replyAll
   295                 destination.originalMessage = message
   296             } else if theId == .segueForward {
   297                 destination.composeMode = .forward
   298                 destination.originalMessage = message
   299             } else if theId == .segueCompose {
   300             }
   301         case .segueHandshake:
   302             guard let destination = segue.destination as? HandshakeViewController else {
   303                 Log.shared.errorAndCrash(component: #function, errorString: "No DVC?")
   304                 break
   305             }
   306             self.title = NSLocalizedString("Message", comment: "Message view title")
   307             destination.appConfig = appConfig
   308             destination.message = message
   309             destination.ratingReEvaluator = ratingReEvaluator
   310             break
   311         case .noSegue:
   312             break
   313         }
   314     }
   315 }
   316 
   317 // MARK: - RatingReEvaluatorDelegate
   318 
   319 extension EmailViewController: RatingReEvaluatorDelegate {
   320     func ratingChanged(message: Message) {
   321         GCD.onMain {
   322             self.showPepRating()
   323         }
   324     }
   325 }
   326 
   327 // MARK: - MessageAttachmentDelegate
   328 
   329 extension EmailViewController: MessageAttachmentDelegate {
   330     override func viewWillTransition(to size: CGSize,
   331                                      with coordinator: UIViewControllerTransitionCoordinator) {
   332         if UI_USER_INTERFACE_IDIOM() == .pad {
   333             documentInteractionController.dismissMenu(animated: false)
   334         }
   335     }
   336 
   337     func didCreateLocally(attachment: Attachment, url: URL, cell: MessageCell, location: CGPoint,
   338                           inView: UIView?) {
   339         documentInteractionController.url = url
   340         let theView = inView ?? cell
   341         let dim: CGFloat = 40
   342         let rect = CGRect.rectAround(center: location, width: dim, height: dim)
   343         documentInteractionController.presentOptionsMenu(from: rect, in: theView, animated: true)
   344     }
   345 
   346     func didTap(cell: MessageCell, attachment: Attachment, location: CGPoint, inView: UIView?) {
   347         let busyState = inView?.displayAsBusy()
   348         let attachmentOp = AttachmentToLocalURLOperation(attachment: attachment)
   349         attachmentOp.completionBlock = { [weak self] in
   350             attachmentOp.completionBlock = nil
   351             GCD.onMain {
   352                 defer {
   353                     if let bState = busyState {
   354                         inView?.stopDisplayingAsBusy(viewBusyState: bState)
   355                     }
   356                 }
   357                 guard let url = attachmentOp.fileURL else {
   358                     return
   359                 }
   360                 self?.didCreateLocally(attachment: attachment, url: url, cell: cell,
   361                                        location: location, inView: inView)
   362             }
   363         }
   364         backgroundQueue.addOperation(attachmentOp)
   365     }
   366 }
   367 
   368 // MARK: - Title View Extension
   369 
   370 extension EmailViewController {
   371 
   372     func saveTitleView() {
   373         self.originalTitleView = self.title
   374     }
   375 
   376     func recoveryInitialTitle() {
   377         self.navigationItem.titleView = nil
   378         self.navigationItem.title = self.originalTitleView
   379         self.title = self.originalTitleView
   380     }
   381 
   382 }