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