pEpForiOS/UI/EmailDisplay/EmailListViewController.swift
author Dirk Zimmermann <dirk@pep-project.org>
Thu, 21 Jul 2016 17:48:18 +0200
changeset 452 5efee166262b
parent 437 4c7e1b466422
child 466 728b1a0c7eae
permissions -rw-r--r--
Send mail: UI
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@275
    30
    override func viewDidLoad() {
dirk@275
    31
        let refreshController = UIRefreshControl.init()
dirk@275
    32
        refreshController.addTarget(self, action: #selector(self.refresh(_:)),
dirk@275
    33
                                    forControlEvents: UIControlEvents.ValueChanged)
dirk@275
    34
        self.refreshControl = refreshController
dirk@356
    35
        UIHelper.variableCellHeightsTableView(self.tableView)
dirk@275
    36
    }
dirk@31
    37
dirk@31
    38
    override func viewWillAppear(animated: Bool) {
dirk@31
    39
        if appConfig == nil {
dirk@31
    40
            if let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate {
dirk@31
    41
                appConfig = appDelegate.appConfig
dirk@31
    42
            }
dirk@273
    43
        }
dirk@209
    44
        prepareFetchRequest()
dirk@209
    45
dirk@437
    46
        let account:IAccount? = appConfig.model.fetchLastAccount()
ana@152
    47
        if (account == nil)  {
dirk@361
    48
            self.performSegueWithIdentifier(segueUserSettings, sender: self)
ana@207
    49
        } else {
dirk@437
    50
            appConfig.currentAccount = account
dirk@365
    51
            PEPUtil.myselfFromAccount(
dirk@365
    52
                account as! Account, block: { identity in
dirk@414
    53
                    Log.infoComponent(self.comp,
dirk@365
    54
                        "myself: \(identity[kPepAddress]) -> \(identity[kPepFingerprint])")
dirk@365
    55
            })
dirk@275
    56
            fetchMailsRefreshControl()
dirk@365
    57
ana@152
    58
        }
ana@207
    59
        super.viewWillAppear(animated)
ana@207
    60
    }
ana@152
    61
dirk@275
    62
    func refresh(refreshControl: UIRefreshControl) {
dirk@275
    63
        fetchMailsRefreshControl(refreshControl)
dirk@275
    64
    }
dirk@275
    65
dirk@275
    66
    func fetchMailsRefreshControl(refreshControl: UIRefreshControl? = nil) {
dirk@115
    67
        if let account = appConfig?.model.fetchLastAccount() {
dirk@55
    68
            let connectInfo = account.connectInfo
dirk@55
    69
dirk@275
    70
            state.isSynching = true
dirk@368
    71
dirk@371
    72
            var operations: [BaseOperation] = []
dirk@371
    73
            if shouldFetchFolders {
dirk@371
    74
                shouldFetchFolders = false
dirk@371
    75
                operations.append(CreateLocalSpecialFoldersOperation.init(
dirk@376
    76
                    coreDataUtil: appConfig.grandOperator.coreDataUtil,
dirk@376
    77
                    accountEmail: account.email))
dirk@371
    78
                operations.append(FetchFoldersOperation.init(
dirk@371
    79
                    grandOperator: appConfig.grandOperator, connectInfo: connectInfo))
dirk@371
    80
            }
dirk@371
    81
            operations.append(PrefetchEmailsOperation.init(
dirk@371
    82
                grandOperator: appConfig.grandOperator, connectInfo: connectInfo,
dirk@371
    83
                folder: ImapSync.defaultImapInboxName))
dirk@371
    84
dirk@371
    85
            appConfig.grandOperator.chainOperations(
dirk@371
    86
                operations, completionBlock: { [unowned self] error in
dirk@58
    87
                    GCD.onMain({
dirk@414
    88
                        Log.infoComponent(self.comp, "Sync completed, error: \(error)")
dirk@325
    89
                        self.appConfig?.model.save()
dirk@275
    90
                        self.state.isSynching = false
dirk@275
    91
                        refreshControl?.endRefreshing()
dirk@149
    92
                        self.updateUI()
dirk@58
    93
                    })
dirk@371
    94
            })
dirk@149
    95
            updateUI()
dirk@55
    96
        }
dirk@31
    97
    }
dirk@31
    98
dirk@196
    99
    @IBAction func newAccountCreatedSegue(segue: UIStoryboardSegue) {
dirk@275
   100
        fetchMailsRefreshControl()
dirk@196
   101
    }
dirk@196
   102
dirk@452
   103
    @IBAction func mailSentSegue(segue: UIStoryboardSegue) {
dirk@452
   104
        print("Mail sent!")
dirk@452
   105
    }
dirk@452
   106
ana@143
   107
    func prepareFetchRequest() {
dirk@324
   108
        let predicates: [NSPredicate] = [NSPredicate.init(format: "bodyFetched = true")]
dirk@31
   109
        let fetchRequest = NSFetchRequest.init(entityName: Message.entityName())
dirk@31
   110
        fetchRequest.predicate = NSCompoundPredicate.init(
dirk@31
   111
            andPredicateWithSubpredicates: predicates)
dirk@324
   112
        fetchRequest.sortDescriptors = [NSSortDescriptor.init(key: "originationDate",
dirk@324
   113
            ascending: false)]
dirk@31
   114
        fetchController = NSFetchedResultsController.init(
dirk@31
   115
            fetchRequest: fetchRequest,
dirk@437
   116
            managedObjectContext: appConfig.coreDataUtil.managedObjectContext,
dirk@31
   117
            sectionNameKeyPath: nil, cacheName: nil)
dirk@31
   118
        fetchController?.delegate = self
dirk@31
   119
        do {
dirk@31
   120
            try fetchController?.performFetch()
dirk@31
   121
        } catch let err as NSError {
dirk@414
   122
            Log.errorComponent(comp, error: err)
dirk@31
   123
        }
dirk@31
   124
    }
dirk@31
   125
dirk@58
   126
    // MARK: - UI State
dirk@58
   127
dirk@58
   128
    func updateUI() {
dirk@275
   129
        if state.isSynching {
dirk@58
   130
            UIApplication.sharedApplication().networkActivityIndicatorVisible = true
dirk@58
   131
        } else {
dirk@58
   132
            UIApplication.sharedApplication().networkActivityIndicatorVisible = false
dirk@58
   133
        }
dirk@58
   134
    }
dirk@58
   135
dirk@31
   136
    // MARK: - UITableViewDataSource
dirk@31
   137
dirk@31
   138
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
dirk@209
   139
        if let count = fetchController?.sections?.count {
dirk@209
   140
            return count
dirk@209
   141
        } else {
dirk@209
   142
            return 0
dirk@209
   143
        }
dirk@31
   144
    }
dirk@31
   145
dirk@31
   146
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
dirk@31
   147
        if fetchController?.sections?.count > 0 {
dirk@31
   148
            if let sections = fetchController?.sections {
dirk@31
   149
                let sectionInfo = sections[section]
dirk@31
   150
                return sectionInfo.numberOfObjects
dirk@31
   151
            }
dirk@31
   152
        }
dirk@31
   153
        return 0
dirk@31
   154
    }
dirk@31
   155
dirk@31
   156
    override func tableView(tableView: UITableView,
dirk@31
   157
                            cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
dirk@209
   158
        let cell = tableView.dequeueReusableCellWithIdentifier(
dirk@209
   159
            "EmailListViewCell", forIndexPath: indexPath) as! EmailListViewCell
dirk@31
   160
        configureCell(cell, indexPath: indexPath)
dirk@31
   161
        return cell
dirk@31
   162
    }
dirk@31
   163
dirk@31
   164
    func configureCell(cell: EmailListViewCell, indexPath: NSIndexPath) {
dirk@31
   165
        if let email = fetchController?.objectAtIndexPath(indexPath) as? Message {
dirk@342
   166
            UIHelper.putString(email.from?.displayString(), toLabel: cell.senderLabel)
dirk@342
   167
            UIHelper.putString(email.subject, toLabel: cell.subjectLabel)
dirk@342
   168
            UIHelper.putString(nil, toLabel: cell.summaryLabel)
dirk@32
   169
dirk@119
   170
            if let originationDate = email.originationDate {
dirk@342
   171
                UIHelper.putString(dateFormatter.stringFromDate(originationDate),
dirk@342
   172
                                   toLabel: cell.dateLabel)
dirk@32
   173
            } else {
dirk@342
   174
                UIHelper.putString(nil, toLabel: cell.dateLabel)
dirk@32
   175
            }
dirk@31
   176
        }
dirk@30
   177
    }
dirk@30
   178
ana@246
   179
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
dirk@361
   180
        if segue.identifier == segueCompose {
dirk@397
   181
            let destination = segue.destinationViewController
dirk@434
   182
                as! ComposeViewController
ana@246
   183
            destination.appConfig = appConfig
dirk@273
   184
        } else if segue.identifier == segueShowEmail {
dirk@273
   185
            guard
dirk@273
   186
                let vc = segue.destinationViewController as? EmailViewController,
dirk@273
   187
                let cell = sender as? UITableViewCell,
dirk@273
   188
                let indexPath = self.tableView.indexPathForCell(cell),
dirk@273
   189
                let email = fetchController?.objectAtIndexPath(indexPath) as? Message else {
dirk@273
   190
                    return
dirk@273
   191
            }
dirk@273
   192
            vc.appConfig = appConfig
dirk@273
   193
            vc.message = email
ana@246
   194
        }
ana@246
   195
    }
dirk@30
   196
}
dirk@30
   197
dirk@31
   198
extension EmailListViewController: NSFetchedResultsControllerDelegate {
dirk@31
   199
    func controllerWillChangeContent(controller: NSFetchedResultsController) {
dirk@31
   200
        tableView.beginUpdates()
dirk@30
   201
    }
dirk@30
   202
dirk@31
   203
    func controller(controller: NSFetchedResultsController,
dirk@31
   204
                    didChangeSection sectionInfo: NSFetchedResultsSectionInfo,
dirk@31
   205
                                     atIndex sectionIndex: Int,
dirk@31
   206
                                             forChangeType type: NSFetchedResultsChangeType) {
dirk@31
   207
        switch (type) {
dirk@31
   208
        case .Insert:
dirk@31
   209
            tableView.insertSections(NSIndexSet.init(index: sectionIndex),
dirk@31
   210
                                     withRowAnimation: .Fade)
dirk@31
   211
        case .Delete:
dirk@31
   212
            tableView.deleteSections(NSIndexSet.init(index: sectionIndex),
dirk@31
   213
                                     withRowAnimation: .Fade)
dirk@31
   214
        default:
dirk@414
   215
            Log.infoComponent(comp, "unhandled changeSectionType: \(type)")
dirk@31
   216
        }
dirk@30
   217
    }
dirk@30
   218
dirk@31
   219
    func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject,
dirk@31
   220
                    atIndexPath indexPath: NSIndexPath?,
dirk@31
   221
                                forChangeType type: NSFetchedResultsChangeType,
dirk@31
   222
                                              newIndexPath: NSIndexPath?) {
dirk@40
   223
        switch type {
dirk@40
   224
        case .Insert:
dirk@40
   225
            tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
dirk@40
   226
        case .Delete:
dirk@40
   227
            tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
dirk@40
   228
        case .Update:
dirk@40
   229
            if let cell = tableView.cellForRowAtIndexPath(indexPath!) {
dirk@40
   230
                self.configureCell(cell as! EmailListViewCell, indexPath: indexPath!)
dirk@40
   231
            } else {
dirk@414
   232
                Log.warnComponent(comp, "Could not find cell for changed indexPath: \(indexPath!)")
dirk@31
   233
            }
dirk@40
   234
        case .Move:
dirk@40
   235
            if newIndexPath != indexPath {
dirk@40
   236
                tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
dirk@40
   237
                tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
dirk@40
   238
            }
dirk@31
   239
        }
dirk@31
   240
    }
dirk@31
   241
dirk@31
   242
    func controllerDidChangeContent(controller: NSFetchedResultsController) {
dirk@31
   243
        tableView.endUpdates()
dirk@31
   244
    }
dirk@30
   245
}