pEpForiOS/UI/EmailDisplay/EmailListViewController.swift
author Ana Rebollo <ana@pep-project.org>
Fri, 26 Aug 2016 17:46:52 +0200
changeset 620 1b83e1fcee8f
parent 615 5bd251bd89f7
child 621 8e0466817d45
permissions -rw-r--r--
IOS-101: code refactor
     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 class EmailListViewController: UITableViewController {
    14     struct EmailListConfig {
    15         let appConfig: AppConfig
    16 
    17         /** Set to whatever criteria you want to have mails displayed */
    18         let predicate: NSPredicate?
    19 
    20         /** The sort descriptors to be used for displaying emails */
    21         let sortDescriptors: [NSSortDescriptor]?
    22 
    23         /** If applicable, the account to refresh from */
    24         let account: IAccount?
    25 
    26         /** If applicable, the folder name to sync */
    27         let folderName: String?
    28 
    29         /** Should there be a sync directly when the view appears? */
    30         let syncOnAppear: Bool
    31     }
    32 
    33     struct UIState {
    34         var isSynching: Bool = false
    35     }
    36     
    37     let comp = "EmailListViewController"
    38 
    39     let segueShowEmail = "segueShowEmail"
    40     let segueCompose = "segueCompose"
    41     let segueUserSettings = "segueUserSettings"
    42 
    43     var config: EmailListConfig!
    44 
    45     var fetchController: NSFetchedResultsController?
    46     var state = UIState()
    47     let dateFormatter = UIHelper.dateFormatterEmailList()
    48 
    49     /**
    50      The default background color for an email cell, as determined the first time a cell is
    51      created.
    52      */
    53     var defaultCellBackgroundColor: UIColor?
    54 
    55     /**
    56      Indicates whether `defaultCellBackgroundColor` has been determined or not.
    57      */
    58     var determinedCellBackgroundColor: Bool = false
    59 
    60     var refreshController: UIRefreshControl!
    61 
    62     func isReadedMessage(message: IMessage)-> Bool {
    63         return true
    64     }
    65 
    66     func isImportantMessage(message: IMessage)-> Bool {
    67         return true
    68     }
    69 
    70     override func viewDidLoad() {
    71         refreshController = UIRefreshControl.init()
    72         refreshController.addTarget(self, action: #selector(self.fetchMailsRefreshControl(_:)),
    73                                     forControlEvents: UIControlEvents.ValueChanged)
    74         UIHelper.variableCellHeightsTableView(self.tableView)
    75     }
    76 
    77     override func viewWillAppear(animated: Bool) {
    78         // Disable fetching if there is no account
    79         if config.account != nil {
    80             self.refreshControl = refreshController
    81         } else {
    82             self.refreshControl = nil
    83         }
    84 
    85         prepareFetchRequest()
    86         if config.syncOnAppear {
    87             fetchMailsRefreshControl()
    88         }
    89         super.viewWillAppear(animated)
    90     }
    91 
    92     func fetchMailsRefreshControl(refreshControl: UIRefreshControl? = nil) {
    93         if let account = config.account {
    94             let connectInfo = account.connectInfo
    95 
    96             state.isSynching = true
    97             updateUI()
    98 
    99             config.appConfig.grandOperator.fetchEmailsAndDecryptConnectInfos(
   100                 [connectInfo], folderName: config.folderName,
   101                 completionBlock: { error in
   102                     Log.infoComponent(self.comp, "Sync completed, error: \(error)")
   103                     if let err = error {
   104                         UIHelper.displayError(err, controller: self)
   105                     }
   106                     self.config.appConfig.model.save()
   107                     self.state.isSynching = false
   108                     refreshControl?.endRefreshing()
   109                     self.updateUI()
   110             })
   111         } else {
   112             state.isSynching = false
   113             updateUI()
   114         }
   115     }
   116 
   117     @IBAction func mailSentSegue(segue: UIStoryboardSegue) {
   118     }
   119 
   120     func prepareFetchRequest() {
   121         let fetchRequest = NSFetchRequest.init(entityName: Message.entityName())
   122         fetchRequest.predicate = config.predicate
   123         fetchRequest.sortDescriptors = config.sortDescriptors
   124         fetchController = NSFetchedResultsController.init(
   125             fetchRequest: fetchRequest,
   126             managedObjectContext: config.appConfig.coreDataUtil.managedObjectContext,
   127             sectionNameKeyPath: nil, cacheName: nil)
   128         fetchController?.delegate = self
   129         do {
   130             try fetchController?.performFetch()
   131         } catch let err as NSError {
   132             Log.errorComponent(comp, error: err)
   133         }
   134     }
   135 
   136     // MARK: - UI State
   137 
   138     func updateUI() {
   139         if state.isSynching {
   140             UIApplication.sharedApplication().networkActivityIndicatorVisible = true
   141         } else {
   142             UIApplication.sharedApplication().networkActivityIndicatorVisible = false
   143             self.refreshControl?.endRefreshing()
   144         }
   145     }
   146 
   147     // MARK: - UITableViewDataSource
   148 
   149     override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
   150         if let count = fetchController?.sections?.count {
   151             return count
   152         } else {
   153             return 0
   154         }
   155     }
   156 
   157     override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
   158         if fetchController?.sections?.count > 0 {
   159             if let sections = fetchController?.sections {
   160                 let sectionInfo = sections[section]
   161                 return sectionInfo.numberOfObjects
   162             }
   163         }
   164         return 0
   165     }
   166 
   167     override func tableView(tableView: UITableView,
   168                             cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
   169         let cell = tableView.dequeueReusableCellWithIdentifier(
   170             "EmailListViewCell", forIndexPath: indexPath) as! EmailListViewCell
   171         if !determinedCellBackgroundColor {
   172             defaultCellBackgroundColor = cell.backgroundColor
   173             determinedCellBackgroundColor = true
   174         }
   175         configureCell(cell, indexPath: indexPath)
   176         return cell
   177     }
   178 
   179     func configureCell(cell: EmailListViewCell, indexPath: NSIndexPath) {
   180         cell.isImportantImage.hidden = true
   181         if let email = fetchController?.objectAtIndexPath(indexPath) as? Message {
   182             if let colorRating = PEPUtil.colorRatingFromInt(email.pepColorRating?.integerValue) {
   183                 let privacyColor = PEPUtil.colorFromPepRating(colorRating)
   184                 if let uiColor = UIHelper.textBackgroundUIColorFromPrivacyColor(privacyColor) {
   185                     cell.backgroundColor = uiColor
   186                 } else {
   187                     if determinedCellBackgroundColor {
   188                         cell.backgroundColor = defaultCellBackgroundColor
   189                     }
   190                 }
   191             }
   192             UIHelper.putString(email.from?.displayString(), toLabel: cell.senderLabel)
   193             UIHelper.putString(email.subject, toLabel: cell.subjectLabel)
   194 
   195             // Snippet
   196             if let text = email.longMessage {
   197                 let theText = text.replaceNewLinesWith(" ").trimmedWhiteSpace()
   198                 UIHelper.putString(theText, toLabel: cell.summaryLabel)
   199             } else if let html = email.longMessageFormatted {
   200                 var text = html.extractTextFromHTML()
   201                 text = text.replaceNewLinesWith(" ").trimmedWhiteSpace()
   202                 UIHelper.putString(text, toLabel: cell.summaryLabel)
   203             } else {
   204                 UIHelper.putString(nil, toLabel: cell.summaryLabel)
   205             }
   206 
   207             if let receivedDate = email.receivedDate {
   208                 UIHelper.putString(dateFormatter.stringFromDate(receivedDate),
   209                                    toLabel: cell.dateLabel)
   210             } else {
   211                 UIHelper.putString(nil, toLabel: cell.dateLabel)
   212             }
   213 
   214             if (isImportantMessage(email)) {
   215                 cell.isImportantImage.hidden = false
   216                 cell.isImportantImage.backgroundColor = UIColor.orangeColor()
   217             }
   218             if (isReadedMessage(email)) {
   219                 cell.isImportantImage.hidden = false
   220                 cell.isImportantImage.backgroundColor = UIColor.blueColor()
   221             }
   222         }
   223     }
   224 
   225     override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
   226         // Make sure the current account is set, if defined
   227         config.appConfig.currentAccount = config.account
   228 
   229         if segue.identifier == segueCompose {
   230             let destination = segue.destinationViewController
   231                 as! ComposeViewController
   232             destination.appConfig = config.appConfig
   233         } else if segue.identifier == segueShowEmail {
   234             guard
   235                 let vc = segue.destinationViewController as? EmailViewController,
   236                 let cell = sender as? UITableViewCell,
   237                 let indexPath = self.tableView.indexPathForCell(cell),
   238                 let email = fetchController?.objectAtIndexPath(indexPath) as? Message else {
   239                     return
   240             }
   241             vc.appConfig = config.appConfig
   242             vc.message = email
   243         }
   244     }
   245 }
   246 
   247 extension EmailListViewController: NSFetchedResultsControllerDelegate {
   248     func controllerWillChangeContent(controller: NSFetchedResultsController) {
   249         tableView.beginUpdates()
   250     }
   251 
   252     func controller(controller: NSFetchedResultsController,
   253                     didChangeSection sectionInfo: NSFetchedResultsSectionInfo,
   254                                      atIndex sectionIndex: Int,
   255                                              forChangeType type: NSFetchedResultsChangeType) {
   256         switch (type) {
   257         case .Insert:
   258             tableView.insertSections(NSIndexSet.init(index: sectionIndex),
   259                                      withRowAnimation: .Fade)
   260         case .Delete:
   261             tableView.deleteSections(NSIndexSet.init(index: sectionIndex),
   262                                      withRowAnimation: .Fade)
   263         default:
   264             Log.infoComponent(comp, "unhandled changeSectionType: \(type)")
   265         }
   266     }
   267 
   268     func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject,
   269                     atIndexPath indexPath: NSIndexPath?,
   270                                 forChangeType type: NSFetchedResultsChangeType,
   271                                               newIndexPath: NSIndexPath?) {
   272         switch type {
   273         case .Insert:
   274             tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
   275         case .Delete:
   276             tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
   277         case .Update:
   278             if let cell = tableView.cellForRowAtIndexPath(indexPath!) {
   279                 self.configureCell(cell as! EmailListViewCell, indexPath: indexPath!)
   280             } else {
   281                 Log.warnComponent(comp, "Could not find cell for changed indexPath: \(indexPath!)")
   282             }
   283         case .Move:
   284             if newIndexPath != indexPath {
   285                 tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
   286                 tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
   287             }
   288         }
   289     }
   290 
   291     func controllerDidChangeContent(controller: NSFetchedResultsController) {
   292         tableView.endUpdates()
   293     }
   294 
   295     func createIsFlagAction(activeFlag: Bool, cell: EmailListViewCell) -> UITableViewRowAction {
   296 
   297         // preparing the title action to show when user swipe
   298         var localizedIsFlagTitle = " "
   299         if (activeFlag) {
   300             localizedIsFlagTitle = NSLocalizedString("Flag",
   301             comment: "Flag button title in swipe action on EmailListViewController")
   302         } else {
   303             localizedIsFlagTitle = NSLocalizedString("Unflag",
   304             comment: "unflag button title in swipe action on EmailListViewController")
   305         }
   306 
   307         // preparing action to trigger when user swipe
   308         let isFlagCompletionHandler: (UITableViewRowAction, NSIndexPath) -> Void =
   309             { (action, indexPath) in
   310                 //cell.isImportantImage.hidden = false
   311                 //cell.isImportantImage.backgroundColor = UIColor.orangeColor()
   312                 // TODO: setImportantMessage()
   313                 self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
   314             }
   315         // creating the action
   316         let isFlagAction = UITableViewRowAction(style: .Default, title: localizedIsFlagTitle,
   317                                                 handler: isFlagCompletionHandler)
   318         // changing default action color
   319         isFlagAction.backgroundColor = UIColor.orangeColor()
   320 
   321         return isFlagAction
   322     }
   323 
   324     func createDeleteAction (cell: EmailListViewCell) -> UITableViewRowAction {
   325 
   326         // preparing the title action to show when user swipe
   327         let localizedDeleteTitle = NSLocalizedString("Erase",
   328         comment: "Erase button title in swipe action on EmailListViewController")
   329 
   330         let deleteCompletionHandler: (UITableViewRowAction, NSIndexPath) -> Void =
   331             { (action, indexPath) in
   332                 let managedObject = self.fetchController?.objectAtIndexPath(indexPath) as? Message
   333                 self.fetchController?.managedObjectContext.deleteObject(managedObject!)
   334             }
   335 
   336         // creating the action
   337         let deleteAction = UITableViewRowAction(style: .Default, title: localizedDeleteTitle,
   338                                                 handler: deleteCompletionHandler)
   339 
   340         return deleteAction
   341     }
   342 
   343     func createIsReadAction (isRead: Bool, cell: EmailListViewCell) -> UITableViewRowAction {
   344 
   345         // preparing the title action to show when user swipe
   346         var localizedisReadTitle = " "
   347         if (isRead) {
   348             localizedisReadTitle = NSLocalizedString("Read",
   349             comment: "Read button title in swipe action on EmailListViewController")
   350         } else {
   351             localizedisReadTitle = NSLocalizedString("Unread",
   352             comment: "Unread button title in swipe action on EmailListViewController")
   353         }
   354 
   355         // creating the action
   356         let isReadCompletionHandler: (UITableViewRowAction, NSIndexPath) -> Void =
   357             { (action, indexPath) in
   358                 //cell.isImportantImage.hidden = false
   359                 //cell.isImportantImage.backgroundColor = UIColor.blueColor()
   360                // TODO: setReadMessage()
   361                 self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
   362             }
   363         let isReadAction = UITableViewRowAction(style: .Default, title: localizedisReadTitle,
   364                                                 handler: isReadCompletionHandler)
   365         isReadAction.backgroundColor = UIColor.blueColor()
   366 
   367         return isReadAction
   368     }
   369 
   370     override func tableView(tableView: UITableView, editActionsForRowAtIndexPath
   371                   indexPath: NSIndexPath)-> [UITableViewRowAction]? {
   372 
   373         let cell = tableView.cellForRowAtIndexPath(indexPath) as! EmailListViewCell
   374         let email = fetchController?.objectAtIndexPath(indexPath) as! Message
   375         let isImportant = isImportantMessage(email)
   376         let isRead = isReadedMessage(email)
   377         let isFlagAction = createIsFlagAction(isImportant, cell: cell)
   378         let deleteAction = createDeleteAction(cell)
   379         let isReadAction = createIsReadAction(isRead, cell: cell)
   380         return [deleteAction,isFlagAction,isReadAction]
   381     }
   382 }