pEpForiOS/UI/EmailDisplay/EmailListViewController.swift
author Dirk Zimmermann <dirk@pep-project.org>
Wed, 24 Aug 2016 12:10:18 +0200
changeset 591 06e031dc9477
parent 590 ec3de67ef11a
child 593 ec1c8c29731c
permissions -rw-r--r--
IOS-108 Display arbitrary folders
     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 accounts to refresh from */
    24         let account: IAccount?
    25 
    26         /** If applicable, the folder name to sync */
    27         let folderName: String?
    28     }
    29 
    30     struct UIState {
    31         var isSynching: Bool = false
    32     }
    33     
    34     let comp = "EmailListViewController"
    35 
    36     let segueShowEmail = "segueShowEmail"
    37     let segueCompose = "segueCompose"
    38     let segueUserSettings = "segueUserSettings"
    39 
    40     var config: EmailListConfig!
    41 
    42     var fetchController: NSFetchedResultsController?
    43     var state = UIState()
    44     let dateFormatter = UIHelper.dateFormatterEmailList()
    45 
    46     /**
    47      The default background color for an email cell, as determined the first time a cell is
    48      created.
    49      */
    50     var defaultCellBackgroundColor: UIColor?
    51 
    52     /**
    53      Indicates whether `defaultCellBackgroundColor` has been determined or not.
    54      */
    55     var determinedCellBackgroundColor: Bool = false
    56 
    57     override func viewDidLoad() {
    58         let refreshController = UIRefreshControl.init()
    59         refreshController.addTarget(self, action: #selector(self.refresh(_:)),
    60                                     forControlEvents: UIControlEvents.ValueChanged)
    61         self.refreshControl = refreshController
    62         UIHelper.variableCellHeightsTableView(self.tableView)
    63     }
    64 
    65     override func viewWillAppear(animated: Bool) {
    66         prepareFetchRequest()
    67         super.viewWillAppear(animated)
    68     }
    69 
    70     func refresh(refreshControl: UIRefreshControl) {
    71         fetchMailsRefreshControl(refreshControl)
    72     }
    73 
    74     func fetchMailsRefreshControl(refreshControl: UIRefreshControl? = nil) {
    75         if let account = config.account {
    76             let connectInfo = account.connectInfo
    77 
    78             state.isSynching = true
    79             updateUI()
    80 
    81             config.appConfig.grandOperator.fetchEmailsAndDecryptConnectInfos(
    82                 [connectInfo], folderName: config.folderName,
    83                 completionBlock: { error in
    84                     Log.infoComponent(self.comp, "Sync completed, error: \(error)")
    85                     if let err = error {
    86                         UIHelper.displayError(err, controller: self)
    87                     }
    88                     self.config.appConfig.model.save()
    89                     self.state.isSynching = false
    90                     refreshControl?.endRefreshing()
    91                     self.updateUI()
    92             })
    93         }
    94     }
    95 
    96     @IBAction func mailSentSegue(segue: UIStoryboardSegue) {
    97     }
    98 
    99     func prepareFetchRequest() {
   100         let fetchRequest = NSFetchRequest.init(entityName: Message.entityName())
   101         fetchRequest.predicate = config.predicate
   102         fetchRequest.sortDescriptors = config.sortDescriptors
   103         fetchController = NSFetchedResultsController.init(
   104             fetchRequest: fetchRequest,
   105             managedObjectContext: config.appConfig.coreDataUtil.managedObjectContext,
   106             sectionNameKeyPath: nil, cacheName: nil)
   107         fetchController?.delegate = self
   108         do {
   109             try fetchController?.performFetch()
   110         } catch let err as NSError {
   111             Log.errorComponent(comp, error: err)
   112         }
   113     }
   114 
   115     // MARK: - UI State
   116 
   117     func updateUI() {
   118         if state.isSynching {
   119             UIApplication.sharedApplication().networkActivityIndicatorVisible = true
   120         } else {
   121             UIApplication.sharedApplication().networkActivityIndicatorVisible = false
   122         }
   123     }
   124 
   125     // MARK: - UITableViewDataSource
   126 
   127     override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
   128         if let count = fetchController?.sections?.count {
   129             return count
   130         } else {
   131             return 0
   132         }
   133     }
   134 
   135     override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
   136         if fetchController?.sections?.count > 0 {
   137             if let sections = fetchController?.sections {
   138                 let sectionInfo = sections[section]
   139                 return sectionInfo.numberOfObjects
   140             }
   141         }
   142         return 0
   143     }
   144 
   145     override func tableView(tableView: UITableView,
   146                             cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
   147         let cell = tableView.dequeueReusableCellWithIdentifier(
   148             "EmailListViewCell", forIndexPath: indexPath) as! EmailListViewCell
   149         if !determinedCellBackgroundColor {
   150             defaultCellBackgroundColor = cell.backgroundColor
   151             determinedCellBackgroundColor = true
   152         }
   153         configureCell(cell, indexPath: indexPath)
   154         return cell
   155     }
   156 
   157     func configureCell(cell: EmailListViewCell, indexPath: NSIndexPath) {
   158         if let email = fetchController?.objectAtIndexPath(indexPath) as? Message {
   159             if let colorRating = PEPUtil.colorRatingFromInt(email.pepColorRating?.integerValue) {
   160                 let privacyColor = PEPUtil.colorFromPepRating(colorRating)
   161                 if let uiColor = UIHelper.textBackgroundUIColorFromPrivacyColor(privacyColor) {
   162                     cell.backgroundColor = uiColor
   163                 } else {
   164                     if determinedCellBackgroundColor {
   165                         cell.backgroundColor = defaultCellBackgroundColor
   166                     }
   167                 }
   168             }
   169             UIHelper.putString(email.from?.displayString(), toLabel: cell.senderLabel)
   170             UIHelper.putString(email.subject, toLabel: cell.subjectLabel)
   171 
   172             // Snippet
   173             if let text = email.longMessage {
   174                 let theText = text.replaceNewLinesWith(" ").trimmedWhiteSpace()
   175                 UIHelper.putString(theText, toLabel: cell.summaryLabel)
   176             } else if let html = email.longMessageFormatted {
   177                 var text = html.extractTextFromHTML()
   178                 text = text.replaceNewLinesWith(" ").trimmedWhiteSpace()
   179                 UIHelper.putString(text, toLabel: cell.summaryLabel)
   180             } else {
   181                 UIHelper.putString(nil, toLabel: cell.summaryLabel)
   182             }
   183 
   184             if let receivedDate = email.receivedDate {
   185                 UIHelper.putString(dateFormatter.stringFromDate(receivedDate),
   186                                    toLabel: cell.dateLabel)
   187             } else {
   188                 UIHelper.putString(nil, toLabel: cell.dateLabel)
   189             }
   190         }
   191     }
   192 
   193     override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
   194         if segue.identifier == segueCompose {
   195             let destination = segue.destinationViewController
   196                 as! ComposeViewController
   197             destination.appConfig = config.appConfig
   198         } else if segue.identifier == segueShowEmail {
   199             guard
   200                 let vc = segue.destinationViewController as? EmailViewController,
   201                 let cell = sender as? UITableViewCell,
   202                 let indexPath = self.tableView.indexPathForCell(cell),
   203                 let email = fetchController?.objectAtIndexPath(indexPath) as? Message else {
   204                     return
   205             }
   206             vc.appConfig = config.appConfig
   207             vc.message = email
   208         }
   209     }
   210 }
   211 
   212 extension EmailListViewController: NSFetchedResultsControllerDelegate {
   213     func controllerWillChangeContent(controller: NSFetchedResultsController) {
   214         tableView.beginUpdates()
   215     }
   216 
   217     func controller(controller: NSFetchedResultsController,
   218                     didChangeSection sectionInfo: NSFetchedResultsSectionInfo,
   219                                      atIndex sectionIndex: Int,
   220                                              forChangeType type: NSFetchedResultsChangeType) {
   221         switch (type) {
   222         case .Insert:
   223             tableView.insertSections(NSIndexSet.init(index: sectionIndex),
   224                                      withRowAnimation: .Fade)
   225         case .Delete:
   226             tableView.deleteSections(NSIndexSet.init(index: sectionIndex),
   227                                      withRowAnimation: .Fade)
   228         default:
   229             Log.infoComponent(comp, "unhandled changeSectionType: \(type)")
   230         }
   231     }
   232 
   233     func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject,
   234                     atIndexPath indexPath: NSIndexPath?,
   235                                 forChangeType type: NSFetchedResultsChangeType,
   236                                               newIndexPath: NSIndexPath?) {
   237         switch type {
   238         case .Insert:
   239             tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
   240         case .Delete:
   241             tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
   242         case .Update:
   243             if let cell = tableView.cellForRowAtIndexPath(indexPath!) {
   244                 self.configureCell(cell as! EmailListViewCell, indexPath: indexPath!)
   245             } else {
   246                 Log.warnComponent(comp, "Could not find cell for changed indexPath: \(indexPath!)")
   247             }
   248         case .Move:
   249             if newIndexPath != indexPath {
   250                 tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
   251                 tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
   252             }
   253         }
   254     }
   255 
   256     func controllerDidChangeContent(controller: NSFetchedResultsController) {
   257         tableView.endUpdates()
   258     }
   259 
   260     override func tableView(tableView: UITableView,
   261                             commitEditingStyle editingStyle: UITableViewCellEditingStyle,
   262                                                forRowAtIndexPath indexPath: NSIndexPath) {
   263         // TODO: Delete from the server and managed errors
   264         if editingStyle == .Delete {
   265             let managedObject = fetchController?.objectAtIndexPath(indexPath) as? Message
   266             fetchController?.managedObjectContext.deleteObject(managedObject!)
   267         }
   268     }
   269 }