pEpForiOS/UI/EmailDisplay/EmailListViewController.swift
author Dirk Zimmermann <dirk@pep-project.org>
Mon, 24 Oct 2016 13:59:15 +0200
branchIOS-230
changeset 854 37867bb83913
parent 848 330b2998f47c
child 855 b5badb794b4e
permissions -rw-r--r--
IOS-230 EmailListViewController
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
dirk@30
    35
dirk@854
    36
class EmailListViewController: UITableViewController {
dirk@591
    37
    struct EmailListConfig {
dirk@591
    38
        let appConfig: AppConfig
dirk@583
    39
dirk@854
    40
        /** The folder to display, if it exists */
dirk@854
    41
        let folder: Folder?
dirk@854
    42
    }
dirk@583
    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()
dirk@342
    57
    let dateFormatter = UIHelper.dateFormatterEmailList()
dirk@275
    58
dirk@502
    59
    /**
dirk@502
    60
     The default background color for an email cell, as determined the first time a cell is
dirk@502
    61
     created.
dirk@502
    62
     */
dirk@502
    63
    var defaultCellBackgroundColor: UIColor?
dirk@502
    64
dirk@502
    65
    /**
dirk@502
    66
     Indicates whether `defaultCellBackgroundColor` has been determined or not.
dirk@502
    67
     */
dirk@502
    68
    var determinedCellBackgroundColor: Bool = false
dirk@502
    69
dirk@594
    70
    var refreshController: UIRefreshControl!
dirk@594
    71
dirk@705
    72
    /**
dirk@705
    73
     The message that should be saved as a draft when compose gets aborted.
dirk@705
    74
     */
dirk@841
    75
    var draftMessageToStore: Message?
dirk@705
    76
dirk@719
    77
    /**
dirk@719
    78
     When the user taps on a draft email, this is the message that was selected
dirk@719
    79
     and should be given to the compose view.
dirk@719
    80
     */
dirk@841
    81
    var draftMessageToCompose: Message?
dirk@719
    82
dirk@745
    83
    required init?(coder aDecoder: NSCoder) {
dirk@745
    84
        super.init(coder: aDecoder)
dirk@745
    85
        self.comp = "EmailListViewController"
dirk@745
    86
    }
dirk@745
    87
dirk@802
    88
    func isReadedMessage(_ message: CdMessage)-> Bool {
dirk@637
    89
        return message.flagSeen.boolValue
ana@614
    90
    }
ana@614
    91
dirk@802
    92
    func isImportantMessage(_ message: CdMessage)-> Bool {
ana@621
    93
        return message.flagFlagged.boolValue
ana@614
    94
    }
ana@614
    95
dirk@275
    96
    override func viewDidLoad() {
dirk@356
    97
        UIHelper.variableCellHeightsTableView(self.tableView)
dirk@275
    98
    }
dirk@31
    99
dirk@784
   100
    override func viewWillAppear(_ animated: Bool) {
dirk@854
   101
        updateModel()
ana@207
   102
        super.viewWillAppear(animated)
ana@207
   103
    }
ana@152
   104
dirk@784
   105
    @IBAction func mailSentSegue(_ segue: UIStoryboardSegue) {
dirk@452
   106
    }
dirk@452
   107
dirk@784
   108
    @IBAction func backFromComposeWithoutSavingDraftSegue(_ segue: UIStoryboardSegue) {
dirk@705
   109
    }
dirk@705
   110
dirk@784
   111
    @IBAction func backFromComposeSaveDraftSegue(_ segue: UIStoryboardSegue) {
dirk@854
   112
        guard let _ = draftMessageToStore else {
dirk@705
   113
            return
dirk@705
   114
        }
dirk@707
   115
dirk@707
   116
        state.isSynching = true
dirk@707
   117
        updateUI()
dirk@707
   118
dirk@854
   119
        // TODO: IOS 222: Save as draft
dirk@705
   120
    }
dirk@705
   121
dirk@854
   122
    func updateModel() {
dirk@31
   123
    }
dirk@31
   124
dirk@58
   125
    // MARK: - UI State
dirk@58
   126
dirk@58
   127
    func updateUI() {
dirk@784
   128
        UIApplication.shared.isNetworkActivityIndicatorVisible = state.isSynching
dirk@748
   129
        if !state.isSynching {
dirk@594
   130
            self.refreshControl?.endRefreshing()
dirk@58
   131
        }
dirk@58
   132
    }
dirk@58
   133
dirk@31
   134
    // MARK: - UITableViewDataSource
dirk@31
   135
dirk@784
   136
    override func numberOfSections(in tableView: UITableView) -> Int {
dirk@854
   137
        if let _ = config.folder {
dirk@854
   138
            return 1
dirk@209
   139
        }
dirk@854
   140
        return 0
dirk@31
   141
    }
dirk@31
   142
dirk@784
   143
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
dirk@854
   144
        if let fol = config.folder {
dirk@854
   145
            return fol.messageCount()
dirk@31
   146
        }
dirk@31
   147
        return 0
dirk@31
   148
    }
dirk@31
   149
dirk@784
   150
    override func tableView(_ tableView: UITableView,
dirk@784
   151
                            cellForRowAt indexPath: IndexPath) -> UITableViewCell {
dirk@784
   152
        let cell = tableView.dequeueReusableCell(
dirk@784
   153
            withIdentifier: "EmailListViewCell", for: indexPath) as! EmailListViewCell
dirk@502
   154
        if !determinedCellBackgroundColor {
dirk@502
   155
            defaultCellBackgroundColor = cell.backgroundColor
dirk@502
   156
            determinedCellBackgroundColor = true
dirk@502
   157
        }
dirk@31
   158
        configureCell(cell, indexPath: indexPath)
dirk@31
   159
        return cell
dirk@31
   160
    }
dirk@31
   161
dirk@719
   162
    // MARK: - UITableViewDelegate
dirk@719
   163
dirk@784
   164
    override func tableView(_ tableView: UITableView,
dirk@784
   165
                            didSelectRowAt indexPath: IndexPath) {
dirk@719
   166
        draftMessageToCompose = nil
dirk@719
   167
dirk@784
   168
        let cell = tableView.cellForRow(at: indexPath)
dirk@719
   169
dirk@854
   170
        if fol = config.folder) {
dirk@784
   171
            if folder.folderType.intValue == FolderType.drafts.rawValue {
dirk@784
   172
                draftMessageToCompose = fetchController?.object(at: indexPath)
dirk@802
   173
                    as? CdMessage
dirk@784
   174
                performSegue(withIdentifier: segueCompose, sender: cell)
dirk@719
   175
                return
dirk@719
   176
            }
dirk@719
   177
        }
dirk@719
   178
dirk@784
   179
        performSegue(withIdentifier: segueShowEmail, sender: cell)
dirk@719
   180
    }
dirk@719
   181
dirk@784
   182
    override func tableView(_ tableView: UITableView, editActionsForRowAt
dirk@784
   183
        indexPath: IndexPath)-> [UITableViewRowAction]? {
dirk@744
   184
dirk@784
   185
        let cell = tableView.cellForRow(at: indexPath) as! EmailListViewCell
dirk@802
   186
        let email = fetchController?.object(at: indexPath) as! CdMessage
dirk@744
   187
dirk@744
   188
        let isFlagAction = createIsFlagAction(email, cell: cell)
dirk@744
   189
        let deleteAction = createDeleteAction(cell)
dirk@744
   190
        let isReadAction = createIsReadAction(email, cell: cell)
dirk@744
   191
        return [deleteAction,isFlagAction,isReadAction]
dirk@744
   192
    }
dirk@744
   193
dirk@719
   194
    // MARK: - Misc
dirk@719
   195
dirk@784
   196
    override func configureCell(_ theCell: UITableViewCell, indexPath: IndexPath) {
dirk@745
   197
        guard let cell = theCell as? EmailListViewCell else {
dirk@745
   198
            return
dirk@745
   199
        }
dirk@802
   200
        if let email = fetchController?.object(at: indexPath) as? CdMessage {
dirk@848
   201
            if let colorRating = PEPUtil.pEpRatingFromInt(email.pepColorRating?.intValue) {
dirk@848
   202
                let privacyColor = PEPUtil.pEpColorFromRating(colorRating)
dirk@502
   203
                if let uiColor = UIHelper.textBackgroundUIColorFromPrivacyColor(privacyColor) {
dirk@502
   204
                    cell.backgroundColor = uiColor
dirk@502
   205
                } else {
dirk@502
   206
                    if determinedCellBackgroundColor {
dirk@502
   207
                        cell.backgroundColor = defaultCellBackgroundColor
dirk@502
   208
                    }
dirk@502
   209
                }
dirk@502
   210
            }
dirk@342
   211
            UIHelper.putString(email.from?.displayString(), toLabel: cell.senderLabel)
dirk@342
   212
            UIHelper.putString(email.subject, toLabel: cell.subjectLabel)
dirk@572
   213
dirk@572
   214
            // Snippet
dirk@565
   215
            if let text = email.longMessage {
dirk@572
   216
                let theText = text.replaceNewLinesWith(" ").trimmedWhiteSpace()
dirk@565
   217
                UIHelper.putString(theText, toLabel: cell.summaryLabel)
dirk@572
   218
            } else if let html = email.longMessageFormatted {
dirk@568
   219
                var text = html.extractTextFromHTML()
dirk@572
   220
                text = text.replaceNewLinesWith(" ").trimmedWhiteSpace()
dirk@568
   221
                UIHelper.putString(text, toLabel: cell.summaryLabel)
dirk@568
   222
            } else {
dirk@568
   223
                UIHelper.putString(nil, toLabel: cell.summaryLabel)
dirk@565
   224
            }
dirk@32
   225
dirk@480
   226
            if let receivedDate = email.receivedDate {
dirk@788
   227
                UIHelper.putString(dateFormatter.string(from: receivedDate as Date),
dirk@342
   228
                                   toLabel: cell.dateLabel)
dirk@32
   229
            } else {
dirk@342
   230
                UIHelper.putString(nil, toLabel: cell.dateLabel)
dirk@32
   231
            }
ana@620
   232
ana@656
   233
            if (isImportantMessage(email) && isReadedMessage(email)) {
dirk@784
   234
                cell.isImportantImage.isHidden = false
dirk@784
   235
                cell.isImportantImage.backgroundColor = UIColor.orange
ana@666
   236
            }
ana@666
   237
            else if (isImportantMessage(email) && !isReadedMessage(email)) {
dirk@784
   238
                cell.isImportantImage.isHidden = false
dirk@784
   239
                cell.isImportantImage.backgroundColor = UIColor.blue
ana@656
   240
                cell.isImportantImage.layer.borderWidth = 2
dirk@784
   241
                cell.isImportantImage.layer.borderColor = UIColor.orange.cgColor
ana@666
   242
            } else if (!isImportantMessage(email) && isReadedMessage(email)) {
dirk@784
   243
                    cell.isImportantImage.isHidden = true
ana@666
   244
            } else if (!isImportantMessage(email) && !isReadedMessage(email)) {
dirk@784
   245
                cell.isImportantImage.isHidden = false
dirk@784
   246
                cell.isImportantImage.backgroundColor = UIColor.blue
ana@620
   247
            }
dirk@31
   248
        }
dirk@30
   249
    }
dirk@30
   250
dirk@784
   251
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
dirk@606
   252
        // Make sure the current account is set, if defined
dirk@606
   253
        config.appConfig.currentAccount = config.account
dirk@606
   254
dirk@361
   255
        if segue.identifier == segueCompose {
dirk@784
   256
            let destination = segue.destination
dirk@434
   257
                as! ComposeViewController
dirk@583
   258
            destination.appConfig = config.appConfig
dirk@719
   259
            if let draft = draftMessageToCompose {
dirk@723
   260
                draft.flagSeen = true
dirk@723
   261
                draft.updateFlags()
dirk@723
   262
                config.appConfig.model.save()
dirk@723
   263
dirk@719
   264
                destination.originalMessage = draft
dirk@784
   265
                destination.composeMode = .composeDraft
dirk@719
   266
            }
dirk@273
   267
        } else if segue.identifier == segueShowEmail {
dirk@273
   268
            guard
dirk@784
   269
                let vc = segue.destination as? EmailViewController,
dirk@273
   270
                let cell = sender as? UITableViewCell,
dirk@784
   271
                let indexPath = self.tableView.indexPath(for: cell),
dirk@802
   272
                let email = fetchController?.object(at: indexPath) as? CdMessage else {
dirk@273
   273
                    return
dirk@273
   274
            }
dirk@583
   275
            vc.appConfig = config.appConfig
dirk@273
   276
            vc.message = email
ana@246
   277
        }
ana@246
   278
    }
dirk@744
   279
dirk@802
   280
    func syncFlagsToServer(_ message: CdMessage) {
dirk@744
   281
        self.config.appConfig.grandOperator.syncFlagsToServerForFolder(
dirk@744
   282
            message.folder,
dirk@744
   283
            completionBlock: { error in
dirk@744
   284
                UIHelper.displayError(error, controller: self)
dirk@744
   285
        })
dirk@744
   286
    }
dirk@744
   287
dirk@802
   288
    func createIsFlagAction(_ message: CdMessage, cell: EmailListViewCell) -> UITableViewRowAction {
dirk@744
   289
dirk@744
   290
        // preparing the title action to show when user swipe
dirk@744
   291
        var localizedIsFlagTitle = " "
dirk@744
   292
        if (isImportantMessage(message)) {
dirk@744
   293
            localizedIsFlagTitle = NSLocalizedString("Unflag",
dirk@744
   294
                                                     comment: "Unflag button title in swipe action on EmailListViewController")
dirk@744
   295
        } else {
dirk@744
   296
            localizedIsFlagTitle = NSLocalizedString("Flag",
dirk@744
   297
                                                     comment: "Flag button title in swipe action on EmailListViewController")
dirk@744
   298
        }
dirk@744
   299
dirk@744
   300
        // preparing action to trigger when user swipe
dirk@784
   301
        let isFlagCompletionHandler: (UITableViewRowAction, IndexPath) -> Void =
dirk@744
   302
            { (action, indexPath) in
dirk@744
   303
                if (self.isImportantMessage(message)) {
dirk@744
   304
                    message.flagFlagged = false
dirk@744
   305
dirk@744
   306
                } else {
dirk@744
   307
                    message.flagFlagged = true
dirk@744
   308
                }
dirk@744
   309
                message.updateFlags()
dirk@744
   310
                self.syncFlagsToServer(message)
dirk@784
   311
                self.tableView.reloadRows(at: [indexPath], with: .none)
dirk@744
   312
        }
dirk@744
   313
        // creating the action
dirk@784
   314
        let isFlagAction = UITableViewRowAction(style: .default, title: localizedIsFlagTitle,
dirk@744
   315
                                                handler: isFlagCompletionHandler)
dirk@744
   316
        // changing default action color
dirk@784
   317
        isFlagAction.backgroundColor = UIColor.orange
dirk@744
   318
dirk@744
   319
        return isFlagAction
dirk@744
   320
    }
dirk@744
   321
dirk@784
   322
    func createDeleteAction (_ cell: EmailListViewCell) -> UITableViewRowAction {
dirk@744
   323
dirk@744
   324
        // preparing the title action to show when user swipe
dirk@744
   325
        let localizedDeleteTitle = NSLocalizedString("Erase",
dirk@744
   326
                                                     comment: "Erase button title in swipe action on EmailListViewController")
dirk@744
   327
dirk@784
   328
        let deleteCompletionHandler: (UITableViewRowAction, IndexPath) -> Void =
dirk@744
   329
            { (action, indexPath) in
dirk@802
   330
                let managedObject = self.fetchController?.object(at: indexPath) as? CdMessage
dirk@744
   331
                managedObject?.flagDeleted = true
dirk@744
   332
                managedObject?.updateFlags()
dirk@744
   333
                self.syncFlagsToServer(managedObject!)
dirk@744
   334
        }
dirk@744
   335
dirk@744
   336
        // creating the action
dirk@784
   337
        let deleteAction = UITableViewRowAction(style: .default, title: localizedDeleteTitle,
dirk@744
   338
                                                handler: deleteCompletionHandler)
dirk@744
   339
        return deleteAction
dirk@744
   340
    }
dirk@744
   341
dirk@802
   342
    func createIsReadAction (_ message: CdMessage, cell: EmailListViewCell) -> UITableViewRowAction {
dirk@744
   343
dirk@744
   344
        // preparing the title action to show when user swipe
dirk@744
   345
        var localizedisReadTitle = " "
dirk@744
   346
        if (isReadedMessage(message)) {
dirk@744
   347
            localizedisReadTitle = NSLocalizedString("Unread",
dirk@744
   348
                                                     comment: "Unread button title in swipe action on EmailListViewController")
dirk@744
   349
        } else {
dirk@744
   350
            localizedisReadTitle = NSLocalizedString("Read",
dirk@744
   351
                                                     comment: "Read button title in swipe action on EmailListViewController")
dirk@744
   352
        }
dirk@744
   353
dirk@744
   354
        // creating the action
dirk@784
   355
        let isReadCompletionHandler: (UITableViewRowAction, IndexPath) -> Void =
dirk@744
   356
            { (action, indexPath) in
dirk@744
   357
                if (self.isReadedMessage(message)) {
dirk@744
   358
                    message.flagSeen = false
dirk@744
   359
                    message.updateFlags()
dirk@744
   360
                } else {
dirk@744
   361
                    message.flagSeen = true
dirk@744
   362
                    message.updateFlags()
dirk@744
   363
                }
dirk@744
   364
                self.syncFlagsToServer(message)
dirk@784
   365
                self.tableView.reloadRows(at: [indexPath], with: .none)
dirk@744
   366
        }
dirk@784
   367
        let isReadAction = UITableViewRowAction(style: .default, title: localizedisReadTitle,
dirk@744
   368
                                                handler: isReadCompletionHandler)
dirk@784
   369
        isReadAction.backgroundColor = UIColor.blue
dirk@744
   370
dirk@744
   371
        return isReadAction
dirk@744
   372
    }
dirk@784
   373
}