pEpForiOS/UI/EmailDisplay/EmailListViewController.swift
author Igor Vojinovic <igor.vojinovic@appculture.com>
Thu, 10 Nov 2016 14:16:21 +0100
changeset 1242 4cecb482ae1c
parent 965 baea625419a9
child 1243 2a9465fd0bfc
permissions -rw-r--r--
IOS-143
     1 //
     2 //  EmailListViewController.swift
     3 //  pEpForiOS
     4 //
     5 //  Created by Dirk Zimmermann on 16/04/16.
     6 //  Copyright © 2016 p≡p Security S.A. All rights reserved.
     7 //
     8 
     9 import Foundation
    10 import UIKit
    11 import CoreData
    12 
    13 import MessageModel
    14 
    15 fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
    16   switch (lhs, rhs) {
    17   case let (l?, r?):
    18     return l < r
    19   case (nil, _?):
    20     return true
    21   default:
    22     return false
    23   }
    24 }
    25 
    26 fileprivate func > <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
    27   switch (lhs, rhs) {
    28   case let (l?, r?):
    29     return l > r
    30   default:
    31     return rhs < lhs
    32   }
    33 }
    34 
    35 
    36 class EmailListViewController: UITableViewController {
    37     struct EmailListConfig {
    38         let appConfig: AppConfig
    39 
    40         let account: Account?
    41 
    42         /** The folder to display, if it exists */
    43         var folder: Folder?
    44     }
    45 
    46     var comp = "EmailListViewController"
    47 
    48     struct UIState {
    49         var isSynching: Bool = false
    50     }
    51     
    52     let segueShowEmail = "segueShowEmail"
    53     let segueCompose = "segueCompose"
    54     let segueUserSettings = "segueUserSettings"
    55 
    56     var config: EmailListConfig!
    57 
    58     var state = UIState()
    59     let dateFormatter = UIHelper.dateFormatterEmailList()
    60 
    61     /**
    62      The default background color for an email cell, as determined the first time a cell is
    63      created.
    64      */
    65     var defaultCellBackgroundColor: UIColor?
    66 
    67     /**
    68      Indicates whether `defaultCellBackgroundColor` has been determined or not.
    69      */
    70     var determinedCellBackgroundColor: Bool = false
    71 
    72     var refreshController: UIRefreshControl!
    73 
    74     /**
    75      The message that should be saved as a draft when compose gets aborted.
    76      */
    77     var draftMessageToStore: Message?
    78 
    79     /**
    80      When the user taps on a draft email, this is the message that was selected
    81      and should be given to the compose view.
    82      */
    83     var draftMessageToCompose: Message?
    84 
    85     required init?(coder aDecoder: NSCoder) {
    86         super.init(coder: aDecoder)
    87         self.comp = "EmailListViewController"
    88     }
    89 
    90     func isRead(message: Message)-> Bool {
    91         return message.imapFlags?.seen ?? false
    92     }
    93 
    94     func isImportant(message: Message)-> Bool {
    95         return message.imapFlags?.flagged ?? false
    96     }
    97 
    98     override func viewDidLoad() {
    99         super.viewDidLoad()
   100         UIHelper.variableCellHeightsTableView(self.tableView)
   101     }
   102 
   103     override func viewWillAppear(_ animated: Bool) {
   104         updateModel()
   105         super.viewWillAppear(animated)
   106     }
   107 
   108     @IBAction func mailSentSegue(_ segue: UIStoryboardSegue) {
   109     }
   110 
   111     @IBAction func backFromComposeWithoutSavingDraftSegue(_ segue: UIStoryboardSegue) {
   112     }
   113 
   114     @IBAction func backFromComposeSaveDraftSegue(_ segue: UIStoryboardSegue) {
   115         guard let message = draftMessageToStore else {
   116             return
   117         }
   118 
   119         state.isSynching = true
   120         updateUI()
   121 
   122         message.imapFlags?.draft = true
   123 
   124         // TODO: IOS 222: Save as draft
   125         if let folder = draftMessageToStore?.parent as? Folder {
   126             if folder.folderType == .drafts {
   127                 message.save()
   128                 return
   129             }
   130         }
   131 
   132         guard let account = config.account else {
   133             return
   134         }
   135         
   136         if let folder = account.folder(ofType: FolderType.drafts) {
   137             folder.save(message: message)
   138             return
   139         }
   140     }
   141 
   142     
   143     @IBAction func showUnreadButtonTapped(_ sender: UIBarButtonItem) {
   144         
   145     }
   146     
   147     func updateModel() {
   148         config.folder = MockData.createFolder(config.account!)
   149     }
   150 
   151     // MARK: - UI State
   152 
   153     func updateUI() {
   154         UIApplication.shared.isNetworkActivityIndicatorVisible = state.isSynching
   155         if !state.isSynching {
   156             self.refreshControl?.endRefreshing()
   157         }
   158     }
   159 
   160     // MARK: - UITableViewDataSource
   161 
   162     override func numberOfSections(in tableView: UITableView) -> Int {
   163         if let _ = config.folder {
   164             return 1
   165         }
   166         return 0
   167     }
   168 
   169     override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
   170         if let fol = config.folder {
   171             return fol.messageCount()
   172         }
   173         return 0
   174     }
   175 
   176     override func tableView(_ tableView: UITableView,
   177                             cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   178         let cell = tableView.dequeueReusableCell(
   179             withIdentifier: "EmailListViewCell", for: indexPath) as! EmailListViewCell
   180         if !determinedCellBackgroundColor {
   181             defaultCellBackgroundColor = cell.backgroundColor
   182             determinedCellBackgroundColor = true
   183         }
   184         configureCell(cell, indexPath: indexPath)
   185         return cell
   186     }
   187 
   188     /**
   189      The message at the given position.
   190      */
   191     func messageAt(indexPath: IndexPath) -> Message? {
   192         if let fol = config.folder {
   193             return fol.messageByIndex(indexPath.row)
   194         }
   195         return nil
   196     }
   197 
   198     // MARK: - UITableViewDelegate
   199 
   200     override func tableView(_ tableView: UITableView,
   201                             didSelectRowAt indexPath: IndexPath) {
   202         draftMessageToCompose = nil
   203 
   204         let cell = tableView.cellForRow(at: indexPath)
   205 
   206         if let fol = config.folder {
   207             if fol.folderType == .drafts {
   208                 draftMessageToCompose = messageAt(indexPath: indexPath)
   209                 performSegue(withIdentifier: segueCompose, sender: cell)
   210                 return
   211             }
   212         }
   213 
   214         performSegue(withIdentifier: segueShowEmail, sender: cell)
   215     }
   216 
   217     override func tableView(_ tableView: UITableView, editActionsForRowAt
   218         indexPath: IndexPath)-> [UITableViewRowAction]? {
   219         let cell = tableView.cellForRow(at: indexPath) as! EmailListViewCell
   220         if let email = messageAt(indexPath: indexPath) {
   221             let isFlagAction = createIsFlagAction(message: email, cell: cell)
   222             let deleteAction = createDeleteAction(cell)
   223             //let isReadAction = createIsReadAction(message: email, cell: cell)
   224             let moreAction = createMoreAction(message: email, cell: cell)
   225             return [deleteAction,isFlagAction,moreAction]
   226         }
   227         return nil
   228     }
   229 
   230     // MARK: - Misc
   231 
   232     func configureCell(_ theCell: UITableViewCell, indexPath: IndexPath) {
   233         guard let cell = theCell as? EmailListViewCell else {
   234             return
   235         }
   236         if let email = messageAt(indexPath: indexPath) {
   237             if let pEpRating = email.pEpRating {
   238                 let privacyColor = PEPUtil.pEpColorFromRating(pEpRating)
   239                 if let uiColor = UIHelper.textBackgroundUIColorFromPrivacyColor(privacyColor) {
   240                     cell.backgroundColor = uiColor
   241                 } else {
   242                     if determinedCellBackgroundColor {
   243                         cell.backgroundColor = defaultCellBackgroundColor
   244                     }
   245                 }
   246             }
   247             UIHelper.putString(email.from?.displayString, toLabel: cell.senderLabel)
   248             UIHelper.putString(email.shortMessage, toLabel: cell.subjectLabel)
   249 
   250             // Snippet
   251             if let text = email.longMessage {
   252                 let theText = text.replaceNewLinesWith(" ").trimmedWhiteSpace()
   253                 UIHelper.putString(UIHelper.cleanHtml(theText), toLabel: cell.summaryLabel)
   254             } else if let html = email.longMessageFormatted {
   255                 var text = html.extractTextFromHTML()
   256                 text = text.replaceNewLinesWith(" ").trimmedWhiteSpace()
   257                 UIHelper.putString(text, toLabel: cell.summaryLabel)
   258             } else {
   259                 UIHelper.putString(nil, toLabel: cell.summaryLabel)
   260             }
   261 
   262             if let receivedDate = email.received {
   263                 UIHelper.putString(dateFormatter.string(from: receivedDate as Date),
   264                                    toLabel: cell.dateLabel)
   265             } else {
   266                 UIHelper.putString(nil, toLabel: cell.dateLabel)
   267             }
   268 
   269             if (isImportant(message: email) && isRead(message: email)) {
   270                 cell.isImportantImage.isHidden = false
   271                 cell.isImportantImage.backgroundColor = UIColor.orange
   272             }
   273             else if (isImportant(message: email) && !isRead(message: email)) {
   274                 cell.isImportantImage.isHidden = false
   275                 cell.isImportantImage.backgroundColor = UIColor.blue
   276                 cell.isImportantImage.layer.borderWidth = 2
   277                 cell.isImportantImage.layer.borderColor = UIColor.orange.cgColor
   278             } else if (!isImportant(message: email) && isRead(message: email)) {
   279                     cell.isImportantImage.isHidden = true
   280             } else if (!isImportant(message: email) && !isRead(message: email)) {
   281                 cell.isImportantImage.isHidden = false
   282                 cell.isImportantImage.backgroundColor = UIColor.blue
   283             }
   284         }
   285     }
   286 
   287     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
   288         // Make sure the current account is set, if defined
   289         config.appConfig.currentAccount = config.account
   290 
   291         if segue.identifier == segueCompose {
   292             let destination = segue.destination
   293                 as! ComposeViewController
   294             destination.appConfig = config.appConfig
   295             if let draft = draftMessageToCompose {
   296                 draft.imapFlags?.seen = true
   297                 config.appConfig.model.save()
   298 
   299                 destination.originalMessage = draft
   300                 destination.composeMode = .composeDraft
   301             }
   302         } else if segue.identifier == segueShowEmail {
   303             guard
   304                 let vc = segue.destination as? EmailViewController,
   305                 let cell = sender as? UITableViewCell,
   306                 let indexPath = self.tableView.indexPath(for: cell),
   307                 let email = messageAt(indexPath: indexPath) else {
   308                     return
   309             }
   310             vc.appConfig = config.appConfig
   311             vc.message = email
   312         }
   313     }
   314 
   315     func syncFlagsToServer(message: Message) {
   316         // TODO: IOS 222: Sync flags back to server
   317     }
   318 
   319     func createIsFlagAction(message: Message, cell: EmailListViewCell) -> UITableViewRowAction {
   320         // preparing the title action to show when user swipe
   321 //        var localizedIsFlagTitle = " "
   322 //        if (isImportant(message: message)) {
   323 //            localizedIsFlagTitle = NSLocalizedString(
   324 //                "Unflag",
   325 //                comment: "Unflag button title in swipe action on EmailListViewController")
   326 //        } else {
   327 //            localizedIsFlagTitle = NSLocalizedString(
   328 //                "Flag",
   329 //                comment: "Flag button title in swipe action on EmailListViewController")
   330 //        }
   331 
   332         // preparing action to trigger when user swipe
   333         let isFlagCompletionHandler: (UITableViewRowAction, IndexPath) -> Void =
   334             { (action, indexPath) in
   335                 if (self.isImportant(message: message)) {
   336                     message.imapFlags?.flagged = false
   337 
   338                 } else {
   339                     message.imapFlags?.flagged = true
   340                 }
   341                 self.syncFlagsToServer(message: message)
   342                 self.tableView.reloadRows(at: [indexPath], with: .none)
   343         }
   344         // creating the action
   345         let isFlagAction = UITableViewRowAction(style: .normal, title: "          ",
   346                                                 handler: isFlagCompletionHandler)
   347         // changing default action color
   348         let swipeFlagImage = UIImage(named: "swipe-flag")
   349         let flagIconColor = UIColor(patternImage: swipeFlagImage!)
   350         isFlagAction.backgroundColor = flagIconColor
   351 
   352         return isFlagAction
   353     }
   354 
   355     func createDeleteAction (_ cell: EmailListViewCell) -> UITableViewRowAction {
   356 
   357         // preparing the title action to show when user swipe
   358 
   359         let deleteCompletionHandler: (UITableViewRowAction, IndexPath) -> Void =
   360             { (action, indexPath) in
   361                 let message = self.messageAt(indexPath: indexPath)
   362                 message?.imapFlags?.deleted = true
   363                 self.syncFlagsToServer(message: message!)
   364         }
   365 
   366         // creating the action
   367         let deleteAction = UITableViewRowAction(style: .normal, title: "          ",
   368                                                 handler: deleteCompletionHandler)
   369         let swipeTrashImage = UIImage(named: "swipe-trash")
   370         let trashIconColor = UIColor(patternImage: swipeTrashImage!)
   371         deleteAction.backgroundColor = trashIconColor
   372         return deleteAction
   373     }
   374 
   375     func createIsReadAction(message: Message, cell: EmailListViewCell) -> UITableViewRowAction {
   376         // preparing the title action to show when user swipe
   377         var localizedisReadTitle = " "
   378         if (isRead(message: message)) {
   379             localizedisReadTitle = NSLocalizedString(
   380                 "Unread",
   381                 comment: "Unread button title in swipe action on EmailListViewController")
   382         } else {
   383             localizedisReadTitle = NSLocalizedString(
   384                 "Read",
   385                 comment: "Read button title in swipe action on EmailListViewController")
   386         }
   387 
   388         // creating the action
   389         let isReadCompletionHandler: (UITableViewRowAction, IndexPath) -> Void =
   390             { (action, indexPath) in
   391                 if (self.isRead(message: message)) {
   392                     message.imapFlags?.seen = false
   393                 } else {
   394                     message.imapFlags?.seen = true
   395                 }
   396                 self.syncFlagsToServer(message: message)
   397                 self.tableView.reloadRows(at: [indexPath], with: .none)
   398         }
   399         let isReadAction = UITableViewRowAction(style: .default, title: localizedisReadTitle,
   400                                                 handler: isReadCompletionHandler)
   401         isReadAction.backgroundColor = UIColor.blue
   402 
   403         return isReadAction
   404     }
   405     
   406     func createMoreAction(message: Message, cell: EmailListViewCell) -> UITableViewRowAction {
   407         let moreCompletitionHandler :(UITableViewRowAction, IndexPath) -> Void = {(action, indexPath) in
   408             self.showMoreActionSheet(cell: cell)
   409         }
   410         let moreAction = UITableViewRowAction(style: .normal, title: "          ", handler: moreCompletitionHandler)
   411         let swipeMoreImage = UIImage(named: "swipe-more")
   412         let moreIconColor = UIColor(patternImage: swipeMoreImage!)
   413         moreAction.backgroundColor = moreIconColor
   414         return moreAction
   415     }
   416     
   417     // MARK: - Action Sheet
   418     
   419     func showMoreActionSheet(cell: EmailListViewCell) {
   420         let alertControler = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
   421         let cancelAction = createCancelAction()
   422         let replyAction = createReplyAction(cell: cell)
   423         let forwardAction = createForwardAction(cell: cell)
   424         let markAction = createMarkAction()
   425         alertControler.addAction(cancelAction)
   426         alertControler.addAction(replyAction)
   427         alertControler.addAction(forwardAction)
   428         alertControler.addAction(markAction)
   429         present(alertControler, animated: true, completion: nil)
   430     }
   431     
   432     // MARK: - Action Sheet Actions
   433 
   434     func createCancelAction() -> UIAlertAction {
   435       return  UIAlertAction(title: "Cancel", style: .cancel) { (action) in}
   436     }
   437     
   438     func createReplyAction(cell: EmailListViewCell) ->  UIAlertAction {
   439         return UIAlertAction(title: "Reply", style: .default) { (action) in
   440             self.performSegue(withIdentifier: self.segueCompose, sender: cell)
   441         }
   442     }
   443     
   444     func createForwardAction(cell: EmailListViewCell) -> UIAlertAction {
   445         return UIAlertAction(title: "Forward", style: .default) { (action) in
   446             self.performSegue(withIdentifier: self.segueCompose, sender: cell)
   447         }
   448     }
   449     
   450     func createMarkAction() -> UIAlertAction {
   451         return UIAlertAction(title: "Mark", style: .default) { (action) in
   452         }
   453     }
   454 }