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