pEpForiOS/UI/EmailDisplay/EmailListViewController.swift
author Dirk Zimmermann <dirk@pep-project.org>
Thu, 16 Feb 2017 19:30:32 +0100
changeset 1725 2ef8f9937e40
parent 1724 004282d9bf61
child 1737 8d234ddb4824
permissions -rw-r--r--
IOS-83 move trust handling to email view
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@810
    12
import MessageModel
dirk@810
    13
igor@1260
    14
struct EmailListConfig {
igor@1338
    15
    var appConfig: AppConfig?
dirk@1333
    16
igor@1260
    17
    /** The folder to display, if it exists */
igor@1260
    18
    var folder: Folder?
igor@1260
    19
}
dirk@30
    20
dirk@854
    21
class EmailListViewController: UITableViewController {
dirk@583
    22
    struct UIState {
dirk@583
    23
        var isSynching: Bool = false
dirk@583
    24
    }
dirk@30
    25
dirk@1344
    26
    var config: EmailListConfig?
dirk@275
    27
    var state = UIState()
igor@1301
    28
    let searchController = UISearchController(searchResultsController: nil)
dirk@1542
    29
    let cellsByMessageID = NSCache<NSString, EmailListViewCell>()
dirk@719
    30
dirk@1724
    31
    /**
dirk@1724
    32
     After trustwords have been invoked, this will be the partner identity that
dirk@1724
    33
     was either confirmed or mistrusted.
dirk@1724
    34
     */
dirk@1724
    35
    var partnerIdentity: Identity?
dirk@1724
    36
dirk@275
    37
    override func viewDidLoad() {
ylandert@935
    38
        super.viewDidLoad()
xavier@1623
    39
igor@1408
    40
        title = "EmailList.title".localized
igor@1284
    41
        UIHelper.emailListTableHeight(self.tableView)
igor@1301
    42
        addSearchBar()
dirk@275
    43
    }
dirk@31
    44
dirk@784
    45
    override func viewWillAppear(_ animated: Bool) {
igor@1301
    46
        super.viewWillAppear(animated)
xavier@1623
    47
dirk@1344
    48
        if MiscUtil.isUnitTest() {
dirk@1344
    49
            return
dirk@1344
    50
        }
dirk@1344
    51
dirk@1344
    52
        initialConfig()
dirk@854
    53
        updateModel()
dirk@1348
    54
dirk@1348
    55
        MessageModelConfig.messageFolderDelegate = self
dirk@1348
    56
    }
dirk@1348
    57
dirk@1348
    58
    override func viewWillDisappear(_ animated: Bool) {
dirk@1348
    59
        super.viewWillDisappear(animated)
dirk@1348
    60
        MessageModelConfig.messageFolderDelegate = nil
igor@1301
    61
    }
xavier@1623
    62
igor@1338
    63
    func initialConfig() {
dirk@1344
    64
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
igor@1338
    65
            return
igor@1338
    66
        }
dirk@1344
    67
dirk@1344
    68
        if config == nil {
igor@1395
    69
            config = EmailListConfig(appConfig: appDelegate.appConfig, folder: Folder.unifiedInbox())
dirk@1344
    70
        }
dirk@1348
    71
        if Account.all().isEmpty {
igor@1338
    72
            performSegue(withIdentifier:.segueAddNewAccount, sender: self)
igor@1338
    73
        }
igor@1338
    74
    }
xavier@1623
    75
igor@1301
    76
    func addSearchBar() {
igor@1301
    77
        searchController.searchResultsUpdater = self
igor@1301
    78
        searchController.dimsBackgroundDuringPresentation = false
igor@1301
    79
        searchController.delegate = self
igor@1301
    80
        definesPresentationContext = true
igor@1301
    81
        tableView.tableHeaderView = searchController.searchBar
igor@1301
    82
        tableView.setContentOffset(CGPoint(x: 0.0, y: 40.0), animated: false)
igor@1301
    83
    }
dirk@705
    84
xavier@1623
    85
dirk@1330
    86
    @IBAction func showUnreadButtonTapped(_ sender: UIBarButtonItem) {}
xavier@1623
    87
dirk@854
    88
    func updateModel() {
igor@1318
    89
        tableView.reloadData()
dirk@31
    90
    }
dirk@31
    91
dirk@58
    92
    // MARK: - UI State
dirk@58
    93
dirk@58
    94
    func updateUI() {
dirk@784
    95
        UIApplication.shared.isNetworkActivityIndicatorVisible = state.isSynching
dirk@748
    96
        if !state.isSynching {
igor@1260
    97
            refreshControl?.endRefreshing()
dirk@58
    98
        }
dirk@58
    99
    }
dirk@58
   100
dirk@31
   101
    // MARK: - UITableViewDataSource
dirk@31
   102
dirk@784
   103
    override func numberOfSections(in tableView: UITableView) -> Int {
dirk@1344
   104
        if let _ = config?.folder {
dirk@854
   105
            return 1
dirk@209
   106
        }
dirk@854
   107
        return 0
dirk@31
   108
    }
dirk@31
   109
dirk@784
   110
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
dirk@1344
   111
        if let fol = config?.folder  {
dirk@854
   112
            return fol.messageCount()
dirk@31
   113
        }
dirk@31
   114
        return 0
dirk@31
   115
    }
dirk@31
   116
dirk@784
   117
    override func tableView(_ tableView: UITableView,
dirk@784
   118
                            cellForRowAt indexPath: IndexPath) -> UITableViewCell {
dirk@784
   119
        let cell = tableView.dequeueReusableCell(
dirk@784
   120
            withIdentifier: "EmailListViewCell", for: indexPath) as! EmailListViewCell
dirk@1692
   121
        if let messageID = cell.configureCell(config: config, indexPath: indexPath) {
dirk@1542
   122
            cellsByMessageID.setObject(cell, forKey: messageID as NSString)
dirk@1542
   123
        }
dirk@31
   124
        return cell
dirk@31
   125
    }
dirk@31
   126
dirk@719
   127
    // MARK: - UITableViewDelegate
dirk@719
   128
dirk@784
   129
    override func tableView(_ tableView: UITableView, editActionsForRowAt
dirk@784
   130
        indexPath: IndexPath)-> [UITableViewRowAction]? {
xavier@1623
   131
dirk@784
   132
        let cell = tableView.cellForRow(at: indexPath) as! EmailListViewCell
igor@1260
   133
        if let email = cell.messageAt(indexPath: indexPath, config: config) {
igor@1576
   134
            let flagAction = createFlagAction(message: email, cell: cell)
dirk@1507
   135
            let deleteAction = createDeleteAction(message: email, cell: cell)
igor@1242
   136
            let moreAction = createMoreAction(message: email, cell: cell)
igor@1576
   137
            return [deleteAction, flagAction, moreAction]
dirk@855
   138
        }
dirk@855
   139
        return nil
dirk@744
   140
    }
dirk@744
   141
dirk@719
   142
    // MARK: - Misc
dirk@719
   143
dirk@1507
   144
    func createRowAction(cell: EmailListViewCell,
xavier@1623
   145
                         image: UIImage?, action: @escaping (UITableViewRowAction, IndexPath) -> Void,
xavier@1623
   146
                         title: String) -> UITableViewRowAction {
dirk@1510
   147
        let rowAction = UITableViewRowAction(
dirk@1510
   148
            style: .normal, title: title, handler: action)
dirk@1507
   149
dirk@1507
   150
        if let theImage = image {
dirk@1507
   151
            let iconColor = UIColor(patternImage: theImage)
dirk@1507
   152
            rowAction.backgroundColor = iconColor
dirk@744
   153
        }
dirk@744
   154
dirk@1507
   155
        return rowAction
dirk@744
   156
    }
dirk@744
   157
dirk@1507
   158
    func createFlagAction(message: Message, cell: EmailListViewCell) -> UITableViewRowAction {
dirk@1507
   159
        func action(action: UITableViewRowAction, indexPath: IndexPath) -> Void {
dirk@1522
   160
            if message.imapFlags == nil {
dirk@1522
   161
                Log.warn(component: #function, content: "message.imapFlags == nil")
dirk@1522
   162
            }
dirk@1605
   163
            if cell.isFlagged(message: message) {
dirk@1507
   164
                message.imapFlags?.flagged = false
dirk@1507
   165
            } else {
dirk@1507
   166
                message.imapFlags?.flagged = true
dirk@1507
   167
            }
dirk@1507
   168
            message.save()
dirk@1507
   169
            self.tableView.reloadRows(at: [indexPath], with: .none)
dirk@1507
   170
        }
dirk@1510
   171
igor@1523
   172
        var title = "\n\nFlag".localized
dirk@1605
   173
        if message.imapFlags?.flagged ?? true {
igor@1530
   174
            title = "\n\nUnFlag".localized
dirk@744
   175
        }
dirk@744
   176
dirk@1510
   177
        return createRowAction(
dirk@1510
   178
            cell: cell, image: UIImage(named: "swipe-flag"), action: action, title: title)
dirk@744
   179
    }
dirk@744
   180
dirk@1507
   181
    func createDeleteAction(message: Message, cell: EmailListViewCell) -> UITableViewRowAction {
dirk@1507
   182
        func action(action: UITableViewRowAction, indexPath: IndexPath) -> Void {
dirk@1507
   183
            guard let message = cell.messageAt(indexPath: indexPath, config: self.config) else {
dirk@1507
   184
                return
dirk@1507
   185
            }
xavier@1623
   186
dirk@1650
   187
            message.delete() // mark for deletion/trash
dirk@1507
   188
            message.save()
igor@1576
   189
            self.tableView.reloadData()
dirk@1507
   190
        }
dirk@744
   191
dirk@1510
   192
        return createRowAction(
dirk@1510
   193
            cell: cell, image: UIImage(named: "swipe-trash"), action: action,
igor@1523
   194
            title: "\n\nDelete".localized)
dirk@1507
   195
    }
dirk@1507
   196
dirk@1507
   197
    func createMarkAsReadAction(message: Message, cell: EmailListViewCell) -> UITableViewRowAction {
dirk@1507
   198
        func action(action: UITableViewRowAction, indexPath: IndexPath) -> Void {
dirk@1605
   199
            if cell.haveSeen(message: message) {
dirk@1507
   200
                message.imapFlags?.seen = false
dirk@1507
   201
            } else {
dirk@1507
   202
                message.imapFlags?.seen = true
dirk@1507
   203
            }
dirk@1507
   204
            self.tableView.reloadRows(at: [indexPath], with: .none)
dirk@744
   205
        }
dirk@1510
   206
dirk@1510
   207
        var title = NSLocalizedString(
dirk@1510
   208
            "Unread", comment: "Unread button title in swipe action on EmailListViewController")
dirk@1605
   209
        if !cell.haveSeen(message: message) {
dirk@1510
   210
            title = NSLocalizedString(
dirk@1510
   211
                "Read", comment: "Read button title in swipe action on EmailListViewController")
dirk@1507
   212
        }
dirk@1507
   213
dirk@1507
   214
        let isReadAction = createRowAction(cell: cell, image: nil, action: action,
dirk@1510
   215
                                           title: title)
dirk@784
   216
        isReadAction.backgroundColor = UIColor.blue
dirk@744
   217
dirk@744
   218
        return isReadAction
dirk@744
   219
    }
xavier@1623
   220
igor@1242
   221
    func createMoreAction(message: Message, cell: EmailListViewCell) -> UITableViewRowAction {
dirk@1510
   222
        func action(action: UITableViewRowAction, indexPath: IndexPath) -> Void {
igor@1242
   223
            self.showMoreActionSheet(cell: cell)
igor@1242
   224
        }
dirk@1510
   225
dirk@1510
   226
        return createRowAction(
dirk@1510
   227
            cell: cell, image: UIImage(named: "swipe-more"), action: action,
igor@1523
   228
            title: "\n\nMore".localized)
igor@1242
   229
    }
xavier@1623
   230
igor@1242
   231
    // MARK: - Action Sheet
xavier@1623
   232
igor@1242
   233
    func showMoreActionSheet(cell: EmailListViewCell) {
igor@1242
   234
        let alertControler = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
igor@1438
   235
        alertControler.view.tintColor = .pEpGreen
igor@1242
   236
        let cancelAction = createCancelAction()
igor@1242
   237
        let replyAction = createReplyAction(cell: cell)
xavier@1623
   238
        let replyAllAction = createReplyAllAction(cell: cell)
igor@1242
   239
        let forwardAction = createForwardAction(cell: cell)
igor@1242
   240
        let markAction = createMarkAction()
igor@1242
   241
        alertControler.addAction(cancelAction)
igor@1242
   242
        alertControler.addAction(replyAction)
xavier@1620
   243
        alertControler.addAction(replyAllAction)
igor@1242
   244
        alertControler.addAction(forwardAction)
igor@1242
   245
        alertControler.addAction(markAction)
igor@1414
   246
        if let popoverPresentationController = alertControler.popoverPresentationController {
igor@1414
   247
            popoverPresentationController.sourceView = cell
igor@1414
   248
        }
igor@1242
   249
        present(alertControler, animated: true, completion: nil)
igor@1242
   250
    }
xavier@1623
   251
igor@1242
   252
    // MARK: - Action Sheet Actions
igor@1242
   253
igor@1242
   254
    func createCancelAction() -> UIAlertAction {
xavier@1623
   255
        return  UIAlertAction(title: "Cancel", style: .cancel) { (action) in}
igor@1242
   256
    }
xavier@1623
   257
igor@1242
   258
    func createReplyAction(cell: EmailListViewCell) ->  UIAlertAction {
igor@1242
   259
        return UIAlertAction(title: "Reply", style: .default) { (action) in
xavier@1623
   260
            // self.performSegue(withIdentifier: self.segueCompose, sender: cell)
igor@1338
   261
            self.performSegue(withIdentifier: .segueCompose, sender: cell)
igor@1242
   262
        }
igor@1242
   263
    }
xavier@1620
   264
xavier@1620
   265
    func createReplyAllAction(cell: EmailListViewCell) ->  UIAlertAction {
xavier@1623
   266
        return UIAlertAction(title: "Reply All", style: .default) { (action) in
xavier@1623
   267
            self.performSegue(withIdentifier: .segueReplyAll, sender: cell)
xavier@1620
   268
        }
xavier@1620
   269
    }
xavier@1620
   270
igor@1242
   271
    func createForwardAction(cell: EmailListViewCell) -> UIAlertAction {
igor@1242
   272
        return UIAlertAction(title: "Forward", style: .default) { (action) in
igor@1338
   273
            //self.performSegue(withIdentifier: self.segueCompose, sender: cell)
xavier@1664
   274
            self.performSegue(withIdentifier: .segueForward, sender: cell)
igor@1242
   275
        }
igor@1242
   276
    }
xavier@1623
   277
igor@1242
   278
    func createMarkAction() -> UIAlertAction {
igor@1242
   279
        return UIAlertAction(title: "Mark", style: .default) { (action) in
igor@1242
   280
        }
igor@1242
   281
    }
xavier@1623
   282
igor@1301
   283
    // MARK: - Content Search
xavier@1623
   284
igor@1301
   285
    func filterContentForSearchText(searchText: String) {
xavier@1623
   286
igor@1301
   287
    }
xavier@1623
   288
dirk@784
   289
}
igor@1301
   290
igor@1301
   291
extension EmailListViewController: UISearchResultsUpdating, UISearchControllerDelegate {
igor@1301
   292
    public func updateSearchResults(for searchController: UISearchController) {
igor@1301
   293
        filterContentForSearchText(searchText: searchController.searchBar.text!)
igor@1301
   294
    }
xavier@1623
   295
igor@1301
   296
    func didDismissSearchController(_ searchController: UISearchController) {
igor@1301
   297
    }
igor@1301
   298
}
igor@1338
   299
igor@1338
   300
// MARK: - Navigation
igor@1338
   301
igor@1338
   302
extension EmailListViewController: SegueHandlerType {
xavier@1623
   303
igor@1338
   304
    // MARK: - SegueHandlerType
xavier@1623
   305
igor@1338
   306
    enum SegueIdentifier: String {
igor@1338
   307
        case segueAddNewAccount
igor@1338
   308
        case segueEditAccounts
igor@1338
   309
        case segueShowEmail
igor@1338
   310
        case segueCompose
xavier@1623
   311
        case segueReplyAll
xavier@1664
   312
        case segueForward
igor@1338
   313
        case noSegue
igor@1338
   314
    }
xavier@1623
   315
igor@1338
   316
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
igor@1338
   317
        switch segueIdentifier(for: segue) {
xavier@1623
   318
        case .segueReplyAll:
xavier@1623
   319
            if let nav = segue.destination as? UINavigationController,
xavier@1623
   320
                let destination = nav.topViewController as? ComposeTableViewController,
xavier@1623
   321
                let cell = sender as? EmailListViewCell,
xavier@1623
   322
                let indexPath = self.tableView.indexPath(for: cell),
xavier@1623
   323
                let email = cell.messageAt(indexPath: indexPath, config: config) {
xavier@1623
   324
                destination.composeMode = .replyAll
xavier@1623
   325
                destination.appConfig = config?.appConfig
xavier@1623
   326
                destination.originalMessage = email
xavier@1623
   327
            }
igor@1338
   328
            break
igor@1338
   329
        case .segueShowEmail:
dirk@1594
   330
            if let vc = segue.destination as? EmailViewController,
igor@1338
   331
                let cell = sender as? EmailListViewCell,
igor@1338
   332
                let indexPath = self.tableView.indexPath(for: cell),
dirk@1594
   333
                let email = cell.messageAt(indexPath: indexPath, config: config) {
xavier@1623
   334
                vc.appConfig = config?.appConfig
xavier@1623
   335
                vc.message = email
igor@1338
   336
            }
igor@1338
   337
            break
xavier@1664
   338
        case .segueForward:
xavier@1664
   339
            if let nav = segue.destination as? UINavigationController,
xavier@1664
   340
                let destination = nav.topViewController as? ComposeTableViewController,
xavier@1664
   341
                let cell = sender as? EmailListViewCell,
xavier@1664
   342
                let indexPath = self.tableView.indexPath(for: cell),
xavier@1664
   343
                let email = cell.messageAt(indexPath: indexPath, config: config) {
xavier@1664
   344
                destination.composeMode = .forward
xavier@1664
   345
                destination.appConfig = config?.appConfig
xavier@1664
   346
                destination.originalMessage = email
xavier@1664
   347
            }
xavier@1664
   348
            break
dirk@1694
   349
        case .segueAddNewAccount, .segueEditAccounts, .segueCompose, .noSegue:
dirk@1694
   350
            break
igor@1338
   351
        }
igor@1338
   352
    }
dirk@1350
   353
dirk@1350
   354
    func didChangeInternal(messageFolder: MessageFolder) {
dirk@1350
   355
        if let folder = config?.folder,
dirk@1350
   356
            let message = messageFolder as? Message,
dirk@1558
   357
            folder.contains(message: message, deletedMessagesAreContained: true) {
dirk@1350
   358
            if let msg = messageFolder as? Message {
dirk@1350
   359
                if msg.isOriginal {
dirk@1535
   360
                    // new message has arrived
dirk@1675
   361
                    if let index = folder.indexOf(message: msg) {
dirk@1675
   362
                        let ip = IndexPath(row: index, section: 0)
dirk@1675
   363
                        tableView.insertRows(at: [ip], with: .automatic)
dirk@1675
   364
                    } else {
dirk@1675
   365
                        tableView.reloadData()
dirk@1675
   366
                    }
dirk@1558
   367
                } else if msg.isGhost {
dirk@1558
   368
                    if let cell = cellsByMessageID.object(forKey: msg.uuid as NSString) {
dirk@1692
   369
                        if let ip = tableView.indexPath(for: cell) {
dirk@1558
   370
                            tableView.deleteRows(at: [ip], with: .automatic)
dirk@1558
   371
                        }
dirk@1558
   372
                    }
dirk@1535
   373
                } else {
dirk@1558
   374
                    // other flags than delete must have been changed
dirk@1542
   375
                    if let cell = cellsByMessageID.object(forKey: msg.uuid as NSString) {
dirk@1542
   376
                        cell.updateFlags(message: message)
dirk@1542
   377
                    }
dirk@1350
   378
                }
dirk@1350
   379
            }
dirk@1350
   380
        }
dirk@1350
   381
    }
igor@1338
   382
}
dirk@1348
   383
dirk@1348
   384
// MARK: - MessageFolderDelegate
dirk@1348
   385
dirk@1348
   386
extension EmailListViewController: MessageFolderDelegate {
dirk@1348
   387
    func didChange(messageFolder: MessageFolder) {
dirk@1675
   388
        GCD.onMainWait {
dirk@1350
   389
            self.didChangeInternal(messageFolder: messageFolder)
dirk@1348
   390
        }
dirk@1348
   391
    }
dirk@1348
   392
}