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