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