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