pEpForiOS/UI/EmailDisplay/EmailListViewController.swift
author igor <igor@pep-project.org>
Tue, 13 Dec 2016 11:46:39 +0100
changeset 1301 110da6d9278b
parent 1284 49a5197a25e5
child 1304 1cbf166d37e9
permissions -rw-r--r--
Added Search Controller and UIRefreshController
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@808
    12
dirk@810
    13
import MessageModel
dirk@810
    14
dirk@784
    15
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
dirk@784
    16
  switch (lhs, rhs) {
dirk@784
    17
  case let (l?, r?):
dirk@784
    18
    return l < r
dirk@784
    19
  case (nil, _?):
dirk@784
    20
    return true
dirk@784
    21
  default:
dirk@784
    22
    return false
dirk@784
    23
  }
dirk@784
    24
}
dirk@784
    25
dirk@784
    26
fileprivate func > <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
dirk@784
    27
  switch (lhs, rhs) {
dirk@784
    28
  case let (l?, r?):
dirk@784
    29
    return l > r
dirk@784
    30
  default:
dirk@784
    31
    return rhs < lhs
dirk@784
    32
  }
dirk@784
    33
}
dirk@784
    34
igor@1260
    35
struct EmailListConfig {
igor@1260
    36
    let appConfig: AppConfig
igor@1260
    37
    let account: Account?
igor@1260
    38
    /** The folder to display, if it exists */
igor@1260
    39
    var folder: Folder?
igor@1260
    40
}
dirk@30
    41
dirk@854
    42
class EmailListViewController: UITableViewController {
igor@1260
    43
    
dirk@854
    44
    var comp = "EmailListViewController"
dirk@58
    45
dirk@583
    46
    struct UIState {
dirk@583
    47
        var isSynching: Bool = false
dirk@583
    48
    }
dirk@583
    49
    
dirk@273
    50
    let segueShowEmail = "segueShowEmail"
dirk@397
    51
    let segueCompose = "segueCompose"
dirk@397
    52
    let segueUserSettings = "segueUserSettings"
dirk@30
    53
dirk@583
    54
    var config: EmailListConfig!
dirk@583
    55
dirk@275
    56
    var state = UIState()
igor@1260
    57
    
dirk@502
    58
igor@1301
    59
    //var refreshController: UIRefreshControl!
dirk@594
    60
dirk@705
    61
    /**
dirk@705
    62
     The message that should be saved as a draft when compose gets aborted.
dirk@705
    63
     */
dirk@841
    64
    var draftMessageToStore: Message?
dirk@705
    65
dirk@719
    66
    /**
dirk@719
    67
     When the user taps on a draft email, this is the message that was selected
dirk@719
    68
     and should be given to the compose view.
dirk@719
    69
     */
dirk@841
    70
    var draftMessageToCompose: Message?
igor@1301
    71
    let searchController = UISearchController(searchResultsController: nil)
dirk@719
    72
dirk@745
    73
    required init?(coder aDecoder: NSCoder) {
dirk@745
    74
        super.init(coder: aDecoder)
dirk@745
    75
        self.comp = "EmailListViewController"
dirk@745
    76
    }
dirk@745
    77
dirk@275
    78
    override func viewDidLoad() {
ylandert@935
    79
        super.viewDidLoad()
igor@1301
    80
        
igor@1284
    81
        UIHelper.emailListTableHeight(self.tableView)
igor@1301
    82
        addSearchBar()
igor@1301
    83
        addRefreshControl()
dirk@275
    84
    }
dirk@31
    85
dirk@784
    86
    override func viewWillAppear(_ animated: Bool) {
igor@1301
    87
        super.viewWillAppear(animated)
igor@1301
    88
        
dirk@854
    89
        updateModel()
igor@1301
    90
    }
igor@1301
    91
    
igor@1301
    92
    func addSearchBar() {
igor@1301
    93
        searchController.searchResultsUpdater = self
igor@1301
    94
        searchController.dimsBackgroundDuringPresentation = false
igor@1301
    95
        searchController.delegate = self
igor@1301
    96
        definesPresentationContext = true
igor@1301
    97
        tableView.tableHeaderView = searchController.searchBar
igor@1301
    98
        tableView.setContentOffset(CGPoint(x: 0.0, y: 40.0), animated: false)
igor@1301
    99
    }
igor@1301
   100
    
igor@1301
   101
    func addRefreshControl() {
igor@1301
   102
        refreshControl = UIRefreshControl()
igor@1301
   103
        refreshControl?.addTarget(self, action: #selector(refreshTableData), for: UIControlEvents.valueChanged)
igor@1301
   104
        self.tableView.addSubview(refreshControl!)
ana@207
   105
    }
ana@152
   106
dirk@784
   107
    @IBAction func mailSentSegue(_ segue: UIStoryboardSegue) {
dirk@452
   108
    }
dirk@452
   109
dirk@784
   110
    @IBAction func backFromComposeWithoutSavingDraftSegue(_ segue: UIStoryboardSegue) {
dirk@705
   111
    }
dirk@705
   112
dirk@784
   113
    @IBAction func backFromComposeSaveDraftSegue(_ segue: UIStoryboardSegue) {
dirk@867
   114
        guard let message = draftMessageToStore else {
dirk@705
   115
            return
dirk@705
   116
        }
dirk@707
   117
dirk@707
   118
        state.isSynching = true
dirk@707
   119
        updateUI()
dirk@707
   120
dirk@965
   121
        message.imapFlags?.draft = true
dirk@867
   122
dirk@854
   123
        // TODO: IOS 222: Save as draft
dirk@867
   124
        if let folder = draftMessageToStore?.parent as? Folder {
dirk@867
   125
            if folder.folderType == .drafts {
dirk@867
   126
                return
dirk@867
   127
            }
dirk@867
   128
        }
dirk@867
   129
dirk@867
   130
        guard let account = config.account else {
dirk@867
   131
            return
dirk@867
   132
        }
xavier@870
   133
        
hernani@1082
   134
        if account.folder(ofType: FolderType.drafts) != nil {
xavier@870
   135
            return
xavier@870
   136
        }
dirk@705
   137
    }
dirk@705
   138
igor@941
   139
    
igor@941
   140
    @IBAction func showUnreadButtonTapped(_ sender: UIBarButtonItem) {
igor@941
   141
        
igor@941
   142
    }
igor@941
   143
    
dirk@854
   144
    func updateModel() {
igor@1260
   145
        config.folder = MockData.createFolder(config.account!)
dirk@31
   146
    }
dirk@31
   147
dirk@58
   148
    // MARK: - UI State
dirk@58
   149
dirk@58
   150
    func updateUI() {
dirk@784
   151
        UIApplication.shared.isNetworkActivityIndicatorVisible = state.isSynching
dirk@748
   152
        if !state.isSynching {
igor@1260
   153
            refreshControl?.endRefreshing()
dirk@58
   154
        }
dirk@58
   155
    }
dirk@58
   156
dirk@31
   157
    // MARK: - UITableViewDataSource
dirk@31
   158
dirk@784
   159
    override func numberOfSections(in tableView: UITableView) -> Int {
dirk@854
   160
        if let _ = config.folder {
dirk@854
   161
            return 1
dirk@209
   162
        }
dirk@854
   163
        return 0
dirk@31
   164
    }
dirk@31
   165
dirk@784
   166
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
dirk@854
   167
        if let fol = config.folder {
dirk@854
   168
            return fol.messageCount()
dirk@31
   169
        }
dirk@31
   170
        return 0
dirk@31
   171
    }
dirk@31
   172
dirk@784
   173
    override func tableView(_ tableView: UITableView,
dirk@784
   174
                            cellForRowAt indexPath: IndexPath) -> UITableViewCell {
dirk@784
   175
        let cell = tableView.dequeueReusableCell(
dirk@784
   176
            withIdentifier: "EmailListViewCell", for: indexPath) as! EmailListViewCell
igor@1260
   177
        cell.configureCell(indexPath: indexPath, config: config)
dirk@31
   178
        return cell
dirk@31
   179
    }
dirk@31
   180
igor@1260
   181
    
dirk@861
   182
dirk@719
   183
    // MARK: - UITableViewDelegate
dirk@719
   184
dirk@784
   185
    override func tableView(_ tableView: UITableView,
dirk@784
   186
                            didSelectRowAt indexPath: IndexPath) {
dirk@719
   187
        draftMessageToCompose = nil
dirk@719
   188
igor@1260
   189
        let cell = tableView.cellForRow(at: indexPath) as! EmailListViewCell
dirk@719
   190
dirk@855
   191
        if let fol = config.folder {
dirk@855
   192
            if fol.folderType == .drafts {
igor@1260
   193
                draftMessageToCompose = cell.messageAt(indexPath: indexPath, config: config)
dirk@784
   194
                performSegue(withIdentifier: segueCompose, sender: cell)
dirk@719
   195
                return
dirk@719
   196
            }
dirk@719
   197
        }
dirk@719
   198
dirk@784
   199
        performSegue(withIdentifier: segueShowEmail, sender: cell)
dirk@719
   200
    }
dirk@719
   201
dirk@784
   202
    override func tableView(_ tableView: UITableView, editActionsForRowAt
dirk@784
   203
        indexPath: IndexPath)-> [UITableViewRowAction]? {
dirk@784
   204
        let cell = tableView.cellForRow(at: indexPath) as! EmailListViewCell
igor@1260
   205
        if let email = cell.messageAt(indexPath: indexPath, config: config) {
dirk@855
   206
            let isFlagAction = createIsFlagAction(message: email, cell: cell)
dirk@855
   207
            let deleteAction = createDeleteAction(cell)
igor@1242
   208
            //let isReadAction = createIsReadAction(message: email, cell: cell)
igor@1242
   209
            let moreAction = createMoreAction(message: email, cell: cell)
igor@1242
   210
            return [deleteAction,isFlagAction,moreAction]
dirk@855
   211
        }
dirk@855
   212
        return nil
dirk@744
   213
    }
dirk@744
   214
dirk@719
   215
    // MARK: - Misc
dirk@719
   216
dirk@784
   217
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
dirk@606
   218
        // Make sure the current account is set, if defined
dirk@606
   219
        config.appConfig.currentAccount = config.account
dirk@606
   220
dirk@361
   221
        if segue.identifier == segueCompose {
yves@1278
   222
            //let destination = segue.destination as! ComposeTableViewController
yves@1278
   223
            // destination.appConfig = config.appConfig
yves@1278
   224
//            if let draft = draftMessageToCompose {
yves@1278
   225
//                draft.imapFlags?.seen = true
yves@1278
   226
//
yves@1278
   227
//                destination.originalMessage = draft
yves@1278
   228
//                destination.composeMode = .draft
yves@1278
   229
//            }
dirk@273
   230
        } else if segue.identifier == segueShowEmail {
dirk@273
   231
            guard
dirk@784
   232
                let vc = segue.destination as? EmailViewController,
igor@1260
   233
                let cell = sender as? EmailListViewCell,
dirk@784
   234
                let indexPath = self.tableView.indexPath(for: cell),
igor@1260
   235
                let email = cell.messageAt(indexPath: indexPath, config: config) else {
dirk@273
   236
                    return
dirk@273
   237
            }
dirk@583
   238
            vc.appConfig = config.appConfig
dirk@273
   239
            vc.message = email
ana@246
   240
        }
ana@246
   241
    }
dirk@744
   242
dirk@855
   243
    func syncFlagsToServer(message: Message) {
dirk@855
   244
        // TODO: IOS 222: Sync flags back to server
dirk@744
   245
    }
dirk@744
   246
dirk@855
   247
    func createIsFlagAction(message: Message, cell: EmailListViewCell) -> UITableViewRowAction {
dirk@744
   248
        // preparing the title action to show when user swipe
igor@1242
   249
//        var localizedIsFlagTitle = " "
igor@1242
   250
//        if (isImportant(message: message)) {
igor@1242
   251
//            localizedIsFlagTitle = NSLocalizedString(
igor@1242
   252
//                "Unflag",
igor@1242
   253
//                comment: "Unflag button title in swipe action on EmailListViewController")
igor@1242
   254
//        } else {
igor@1242
   255
//            localizedIsFlagTitle = NSLocalizedString(
igor@1242
   256
//                "Flag",
igor@1242
   257
//                comment: "Flag button title in swipe action on EmailListViewController")
igor@1242
   258
//        }
dirk@744
   259
dirk@744
   260
        // preparing action to trigger when user swipe
dirk@784
   261
        let isFlagCompletionHandler: (UITableViewRowAction, IndexPath) -> Void =
dirk@744
   262
            { (action, indexPath) in
igor@1260
   263
                if (cell.isImportant(message: message)) {
dirk@965
   264
                    message.imapFlags?.flagged = false
dirk@744
   265
dirk@744
   266
                } else {
dirk@965
   267
                    message.imapFlags?.flagged = true
dirk@744
   268
                }
dirk@855
   269
                self.syncFlagsToServer(message: message)
dirk@784
   270
                self.tableView.reloadRows(at: [indexPath], with: .none)
dirk@744
   271
        }
dirk@744
   272
        // creating the action
igor@1242
   273
        let isFlagAction = UITableViewRowAction(style: .normal, title: "          ",
dirk@744
   274
                                                handler: isFlagCompletionHandler)
dirk@744
   275
        // changing default action color
igor@1242
   276
        let swipeFlagImage = UIImage(named: "swipe-flag")
igor@1242
   277
        let flagIconColor = UIColor(patternImage: swipeFlagImage!)
igor@1242
   278
        isFlagAction.backgroundColor = flagIconColor
dirk@744
   279
dirk@744
   280
        return isFlagAction
dirk@744
   281
    }
dirk@744
   282
dirk@784
   283
    func createDeleteAction (_ cell: EmailListViewCell) -> UITableViewRowAction {
dirk@744
   284
dirk@744
   285
        // preparing the title action to show when user swipe
dirk@744
   286
dirk@784
   287
        let deleteCompletionHandler: (UITableViewRowAction, IndexPath) -> Void =
dirk@744
   288
            { (action, indexPath) in
igor@1260
   289
                let message = cell.messageAt(indexPath: indexPath, config: self.config)
dirk@965
   290
                message?.imapFlags?.deleted = true
dirk@855
   291
                self.syncFlagsToServer(message: message!)
dirk@744
   292
        }
dirk@744
   293
dirk@744
   294
        // creating the action
igor@1242
   295
        let deleteAction = UITableViewRowAction(style: .normal, title: "          ",
dirk@744
   296
                                                handler: deleteCompletionHandler)
igor@1242
   297
        let swipeTrashImage = UIImage(named: "swipe-trash")
igor@1242
   298
        let trashIconColor = UIColor(patternImage: swipeTrashImage!)
igor@1242
   299
        deleteAction.backgroundColor = trashIconColor
dirk@744
   300
        return deleteAction
dirk@744
   301
    }
dirk@744
   302
dirk@855
   303
    func createIsReadAction(message: Message, cell: EmailListViewCell) -> UITableViewRowAction {
dirk@744
   304
        // preparing the title action to show when user swipe
dirk@744
   305
        var localizedisReadTitle = " "
igor@1260
   306
        if (cell.isRead(message: message)) {
dirk@855
   307
            localizedisReadTitle = NSLocalizedString(
dirk@855
   308
                "Unread",
dirk@855
   309
                comment: "Unread button title in swipe action on EmailListViewController")
dirk@744
   310
        } else {
dirk@855
   311
            localizedisReadTitle = NSLocalizedString(
dirk@855
   312
                "Read",
dirk@855
   313
                comment: "Read button title in swipe action on EmailListViewController")
dirk@744
   314
        }
dirk@744
   315
dirk@744
   316
        // creating the action
dirk@784
   317
        let isReadCompletionHandler: (UITableViewRowAction, IndexPath) -> Void =
dirk@744
   318
            { (action, indexPath) in
igor@1260
   319
                if (cell.isRead(message: message)) {
dirk@965
   320
                    message.imapFlags?.seen = false
dirk@744
   321
                } else {
dirk@965
   322
                    message.imapFlags?.seen = true
dirk@744
   323
                }
dirk@855
   324
                self.syncFlagsToServer(message: message)
dirk@784
   325
                self.tableView.reloadRows(at: [indexPath], with: .none)
dirk@744
   326
        }
dirk@784
   327
        let isReadAction = UITableViewRowAction(style: .default, title: localizedisReadTitle,
dirk@744
   328
                                                handler: isReadCompletionHandler)
dirk@784
   329
        isReadAction.backgroundColor = UIColor.blue
dirk@744
   330
dirk@744
   331
        return isReadAction
dirk@744
   332
    }
igor@1242
   333
    
igor@1242
   334
    func createMoreAction(message: Message, cell: EmailListViewCell) -> UITableViewRowAction {
igor@1242
   335
        let moreCompletitionHandler :(UITableViewRowAction, IndexPath) -> Void = {(action, indexPath) in
igor@1242
   336
            self.showMoreActionSheet(cell: cell)
igor@1242
   337
        }
igor@1242
   338
        let moreAction = UITableViewRowAction(style: .normal, title: "          ", handler: moreCompletitionHandler)
igor@1242
   339
        let swipeMoreImage = UIImage(named: "swipe-more")
igor@1242
   340
        let moreIconColor = UIColor(patternImage: swipeMoreImage!)
igor@1242
   341
        moreAction.backgroundColor = moreIconColor
igor@1242
   342
        return moreAction
igor@1242
   343
    }
igor@1242
   344
    
igor@1242
   345
    // MARK: - Action Sheet
igor@1242
   346
    
igor@1242
   347
    func showMoreActionSheet(cell: EmailListViewCell) {
igor@1242
   348
        let alertControler = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
igor@1242
   349
        let cancelAction = createCancelAction()
igor@1242
   350
        let replyAction = createReplyAction(cell: cell)
igor@1242
   351
        let forwardAction = createForwardAction(cell: cell)
igor@1242
   352
        let markAction = createMarkAction()
igor@1242
   353
        alertControler.addAction(cancelAction)
igor@1242
   354
        alertControler.addAction(replyAction)
igor@1242
   355
        alertControler.addAction(forwardAction)
igor@1242
   356
        alertControler.addAction(markAction)
igor@1242
   357
        present(alertControler, animated: true, completion: nil)
igor@1242
   358
    }
igor@1242
   359
    
igor@1242
   360
    // MARK: - Action Sheet Actions
igor@1242
   361
igor@1242
   362
    func createCancelAction() -> UIAlertAction {
igor@1242
   363
      return  UIAlertAction(title: "Cancel", style: .cancel) { (action) in}
igor@1242
   364
    }
igor@1242
   365
    
igor@1242
   366
    func createReplyAction(cell: EmailListViewCell) ->  UIAlertAction {
igor@1242
   367
        return UIAlertAction(title: "Reply", style: .default) { (action) in
igor@1242
   368
            self.performSegue(withIdentifier: self.segueCompose, sender: cell)
igor@1242
   369
        }
igor@1242
   370
    }
igor@1242
   371
    
igor@1242
   372
    func createForwardAction(cell: EmailListViewCell) -> UIAlertAction {
igor@1242
   373
        return UIAlertAction(title: "Forward", style: .default) { (action) in
igor@1242
   374
            self.performSegue(withIdentifier: self.segueCompose, sender: cell)
igor@1242
   375
        }
igor@1242
   376
    }
igor@1242
   377
    
igor@1242
   378
    func createMarkAction() -> UIAlertAction {
igor@1242
   379
        return UIAlertAction(title: "Mark", style: .default) { (action) in
igor@1242
   380
        }
igor@1242
   381
    }
igor@1301
   382
    
igor@1301
   383
    // MARK: - Content Search
igor@1301
   384
    
igor@1301
   385
    func filterContentForSearchText(searchText: String) {
igor@1301
   386
        
igor@1301
   387
    }
igor@1301
   388
    
igor@1301
   389
    // MARK: - Refresh Table Data
igor@1301
   390
    
igor@1301
   391
    func refreshTableData() {
igor@1301
   392
        refreshControl?.beginRefreshing()
igor@1301
   393
        refreshControl?.endRefreshing()
igor@1301
   394
    }
igor@1301
   395
dirk@784
   396
}
igor@1301
   397
igor@1301
   398
extension EmailListViewController: UISearchResultsUpdating, UISearchControllerDelegate {
igor@1301
   399
    public func updateSearchResults(for searchController: UISearchController) {
igor@1301
   400
        filterContentForSearchText(searchText: searchController.searchBar.text!)
igor@1301
   401
    }
igor@1301
   402
    
igor@1301
   403
    func didDismissSearchController(_ searchController: UISearchController) {
igor@1301
   404
    }
igor@1301
   405
}