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 UIKit
|
dirk@810
|
10 |
import MessageModel
|
dirk@810
|
11 |
|
andreas@3151
|
12 |
class EmailListViewController: BaseTableViewController {
|
andreas@3151
|
13 |
private var _folderToShow: Folder?
|
andreas@3151
|
14 |
var folderToShow: Folder? {
|
andreas@3151
|
15 |
set {
|
andreas@3151
|
16 |
if newValue === _folderToShow {
|
andreas@3151
|
17 |
return
|
andreas@3151
|
18 |
}
|
andreas@3151
|
19 |
if newValue == nil {
|
andreas@3151
|
20 |
model = nil
|
andreas@3151
|
21 |
_folderToShow = newValue
|
andreas@3151
|
22 |
return
|
andreas@3151
|
23 |
}
|
andreas@3151
|
24 |
_folderToShow = newValue
|
andreas@3164
|
25 |
// Update the model to data of new folder/filter
|
andreas@3151
|
26 |
resetModel()
|
andreas@3151
|
27 |
}
|
andreas@3151
|
28 |
get {
|
andreas@3151
|
29 |
Log.shared.errorAndCrash(component: #function,
|
andreas@3151
|
30 |
errorString: "Use only the folderToShow from model (model?folderToShow).")
|
andreas@3151
|
31 |
return _folderToShow
|
andreas@3151
|
32 |
}
|
andreas@3151
|
33 |
}
|
dirk@2854
|
34 |
|
andreas@3151
|
35 |
func updateLastLookAt() {
|
andreas@3151
|
36 |
guard let saveFolder = model?.folderToShow else {
|
andreas@3151
|
37 |
return
|
andreas@3151
|
38 |
}
|
andreas@3151
|
39 |
saveFolder.updateLastLookAt()
|
andreas@3151
|
40 |
}
|
dirk@1906
|
41 |
|
andreas@3151
|
42 |
private var model: EmailListViewModel?
|
dirk@30
|
43 |
|
andreas@3151
|
44 |
private let queue: OperationQueue = {
|
andreas@3151
|
45 |
let createe = OperationQueue()
|
andreas@3151
|
46 |
createe.qualityOfService = .userInteractive
|
andreas@3151
|
47 |
createe.maxConcurrentOperationCount = 10
|
andreas@3151
|
48 |
return createe
|
andreas@3151
|
49 |
}()
|
andreas@3151
|
50 |
private var operations = [IndexPath:Operation]()
|
andreas@2838
|
51 |
public static let storyboardId = "EmailListViewController"
|
andreas@3151
|
52 |
fileprivate var lastSelectedIndexPath: IndexPath?
|
andreas@3151
|
53 |
|
igor@1301
|
54 |
let searchController = UISearchController(searchResultsController: nil)
|
dirk@719
|
55 |
|
andreas@3151
|
56 |
// MARK: - Outlets
|
dirk@1724
|
57 |
|
dirk@2067
|
58 |
@IBOutlet weak var enableFilterButton: UIBarButtonItem!
|
dirk@2067
|
59 |
@IBOutlet weak var textFilterButton: UIBarButtonItem!
|
andreas@3151
|
60 |
@IBOutlet var showFoldersButton: UIBarButtonItem!
|
dirk@2067
|
61 |
|
andreas@3151
|
62 |
// MARK: - Life Cycle
|
dirk@2067
|
63 |
|
dirk@275
|
64 |
override func viewDidLoad() {
|
ylandert@935
|
65 |
super.viewDidLoad()
|
dirk@2189
|
66 |
title = NSLocalizedString("Inbox", comment: "General name for (unified) inbox")
|
igor@1284
|
67 |
UIHelper.emailListTableHeight(self.tableView)
|
andreas@3163
|
68 |
self.textFilterButton.isEnabled = false
|
igor@1301
|
69 |
addSearchBar()
|
dirk@275
|
70 |
}
|
dirk@31
|
71 |
|
dirk@784
|
72 |
override func viewWillAppear(_ animated: Bool) {
|
igor@1301
|
73 |
super.viewWillAppear(animated)
|
xavier@2325
|
74 |
self.navigationController?.setToolbarHidden(false, animated: true)
|
dirk@1344
|
75 |
if MiscUtil.isUnitTest() {
|
dirk@1344
|
76 |
return
|
dirk@1344
|
77 |
}
|
dirk@1344
|
78 |
|
andreas@3151
|
79 |
//BUFF: TODO
|
andreas@3151
|
80 |
if let vm = model {
|
andreas@3163
|
81 |
self.textFilterButton.isEnabled = vm.isFilterEnabled
|
xavier@2967
|
82 |
updateFilterText()
|
xavier@2363
|
83 |
} else {
|
xavier@2363
|
84 |
self.textFilterButton.isEnabled = false
|
xavier@2363
|
85 |
}
|
xavier@1832
|
86 |
|
dirk@1826
|
87 |
setDefaultColors()
|
andreas@3151
|
88 |
setup()
|
andreas@3166
|
89 |
updateView()
|
dirk@1348
|
90 |
|
dirk@2191
|
91 |
// Mark this folder as having been looked at by the user
|
andreas@3151
|
92 |
updateLastLookAt()
|
andreas@3151
|
93 |
setupFoldersBarButton()
|
andreas@3151
|
94 |
}
|
xavier@2585
|
95 |
|
andreas@3151
|
96 |
// MARK: - NavigationBar
|
andreas@3151
|
97 |
|
andreas@3151
|
98 |
private func hideFoldersNavigationBarButton() {
|
andreas@3151
|
99 |
self.showFoldersButton.isEnabled = false
|
andreas@3151
|
100 |
self.showFoldersButton.tintColor = UIColor.clear
|
andreas@3151
|
101 |
}
|
andreas@3151
|
102 |
|
andreas@3151
|
103 |
private func showFoldersNavigationBarButton() {
|
andreas@3151
|
104 |
self.showFoldersButton.isEnabled = true
|
andreas@3151
|
105 |
self.showFoldersButton.tintColor = nil
|
andreas@3151
|
106 |
}
|
andreas@3151
|
107 |
|
andreas@3151
|
108 |
private func resetModel() {
|
andreas@3151
|
109 |
if _folderToShow != nil {
|
andreas@3151
|
110 |
model = EmailListViewModel(delegate: self, folderToShow: _folderToShow)
|
xavier@2585
|
111 |
}
|
dirk@1348
|
112 |
}
|
dirk@1348
|
113 |
|
andreas@3151
|
114 |
private func setup() {
|
andreas@3151
|
115 |
// We have not been created to show a specific folder, thus we show unified inbox
|
andreas@3151
|
116 |
if model?.folderToShow == nil {
|
andreas@3151
|
117 |
folderToShow = UnifiedInbox()
|
andreas@3151
|
118 |
}
|
andreas@3151
|
119 |
|
andreas@3151
|
120 |
if noAccountsExist() {
|
andreas@3151
|
121 |
performSegue(withIdentifier:.segueAddNewAccount, sender: self)
|
andreas@3151
|
122 |
}
|
andreas@3151
|
123 |
self.title = realNameOfFolderToShow()
|
andreas@2683
|
124 |
}
|
xavier@1948
|
125 |
|
andreas@3151
|
126 |
private func noAccountsExist() -> Bool {
|
andreas@3151
|
127 |
return Account.all().isEmpty
|
igor@1301
|
128 |
}
|
xavier@1623
|
129 |
|
andreas@3151
|
130 |
private func setupFoldersBarButton() {
|
andreas@3151
|
131 |
if let size = navigationController?.viewControllers.count, size > 1 {
|
andreas@3151
|
132 |
hideFoldersNavigationBarButton()
|
andreas@3151
|
133 |
} else {
|
andreas@3151
|
134 |
showFoldersNavigationBarButton()
|
xavier@2132
|
135 |
}
|
igor@1338
|
136 |
}
|
xavier@1623
|
137 |
|
andreas@3151
|
138 |
private func addSearchBar() {
|
igor@1301
|
139 |
searchController.searchResultsUpdater = self
|
igor@1301
|
140 |
searchController.dimsBackgroundDuringPresentation = false
|
igor@1301
|
141 |
searchController.delegate = self
|
igor@1301
|
142 |
definesPresentationContext = true
|
igor@1301
|
143 |
tableView.tableHeaderView = searchController.searchBar
|
igor@1301
|
144 |
tableView.setContentOffset(CGPoint(x: 0.0, y: 40.0), animated: false)
|
igor@1301
|
145 |
}
|
dirk@705
|
146 |
|
andreas@3151
|
147 |
// MARK: - Other
|
andreas@3151
|
148 |
|
andreas@3151
|
149 |
private func realNameOfFolderToShow() -> String? {
|
andreas@3151
|
150 |
return model?.folderToShow?.realName
|
andreas@2697
|
151 |
}
|
xavier@2363
|
152 |
|
andreas@3151
|
153 |
private func configure(cell: EmailListViewCell, for indexPath: IndexPath) {
|
andreas@3151
|
154 |
// Configure lightweight stuff on main thread ...
|
andreas@3151
|
155 |
guard let saveModel = model else {
|
andreas@3151
|
156 |
return
|
xavier@2967
|
157 |
}
|
andreas@3151
|
158 |
guard let row = saveModel.row(for: indexPath) else {
|
andreas@3151
|
159 |
Log.shared.errorAndCrash(component: #function, errorString: "We should have a row here")
|
andreas@3151
|
160 |
return
|
andreas@3151
|
161 |
}
|
andreas@3151
|
162 |
cell.senderLabel.text = row.from
|
andreas@3151
|
163 |
cell.subjectLabel.text = row.subject
|
andreas@3151
|
164 |
cell.summaryLabel.text = row.bodyPeek
|
andreas@3151
|
165 |
cell.isFlagged = row.isFlagged
|
andreas@3151
|
166 |
cell.isSeen = row.isSeen
|
andreas@3151
|
167 |
cell.hasAttachment = row.showAttchmentIcon
|
andreas@3151
|
168 |
cell.dateLabel.text = row.dateText
|
andreas@3151
|
169 |
// Set image from cache if any
|
andreas@3151
|
170 |
cell.setContactImage(image: row.senderContactImage)
|
andreas@3151
|
171 |
|
andreas@3151
|
172 |
let op = BlockOperation() { [weak self] in
|
andreas@3151
|
173 |
// ... and expensive computations in background
|
andreas@3151
|
174 |
guard let strongSelf = self else {
|
andreas@3151
|
175 |
// View is gone, nothing to do.
|
andreas@3151
|
176 |
return
|
andreas@3151
|
177 |
}
|
andreas@3151
|
178 |
|
andreas@3151
|
179 |
var senderImage: UIImage?
|
andreas@3151
|
180 |
if row.senderContactImage == nil {
|
andreas@3151
|
181 |
// image for identity has not been cached yet, get and cache it
|
andreas@3151
|
182 |
senderImage = strongSelf.model?.senderImage(forCellAt: indexPath)
|
andreas@3151
|
183 |
}
|
andreas@3151
|
184 |
|
andreas@3151
|
185 |
// Set data on cell on main queue.
|
andreas@3151
|
186 |
// In theory we want to set all data in *one* async call. But as pEpRatingColorImage takes
|
andreas@3151
|
187 |
// very long, we are setting the sender image seperatelly.
|
andreas@3151
|
188 |
DispatchQueue.main.async {
|
andreas@3151
|
189 |
if senderImage != nil {
|
andreas@3151
|
190 |
cell.contactImageView.image = senderImage
|
andreas@3151
|
191 |
}
|
andreas@3151
|
192 |
}
|
andreas@3151
|
193 |
|
andreas@3151
|
194 |
let pEpRatingImage = strongSelf.model?.pEpRatingColorImage(forCellAt: indexPath)
|
andreas@3151
|
195 |
|
andreas@3151
|
196 |
// Set data on cell on main queue, again ...
|
andreas@3151
|
197 |
DispatchQueue.main.async {
|
andreas@3151
|
198 |
if pEpRatingImage != nil {
|
andreas@3151
|
199 |
cell.setPepRatingImage(image: pEpRatingImage)
|
andreas@3151
|
200 |
}
|
andreas@3151
|
201 |
}
|
andreas@3151
|
202 |
}
|
andreas@3151
|
203 |
queue(operation: op, for: indexPath)
|
xavier@2967
|
204 |
}
|
xavier@2967
|
205 |
|
andreas@3151
|
206 |
// MARK: - Actions
|
xavier@1832
|
207 |
|
andreas@3151
|
208 |
@IBAction func filterButtonHasBeenPressed(_ sender: UIBarButtonItem) {
|
andreas@3163
|
209 |
guard let vm = model else {
|
andreas@3163
|
210 |
Log.shared.errorAndCrash(component: #function, errorString: "We should have a model here")
|
andreas@3163
|
211 |
return
|
andreas@3163
|
212 |
}
|
andreas@3163
|
213 |
vm.isFilterEnabled = !vm.isFilterEnabled
|
andreas@3163
|
214 |
upadteFilterButtonView()
|
xavier@1832
|
215 |
}
|
xavier@1832
|
216 |
|
andreas@3163
|
217 |
func upadteFilterButtonView() {
|
andreas@3163
|
218 |
guard let vm = model else {
|
andreas@3163
|
219 |
Log.shared.errorAndCrash(component: #function, errorString: "We should have a model here")
|
andreas@3163
|
220 |
return
|
andreas@3163
|
221 |
}
|
andreas@3163
|
222 |
|
andreas@3163
|
223 |
textFilterButton.isEnabled = vm.isFilterEnabled
|
andreas@3163
|
224 |
if textFilterButton.isEnabled {
|
andreas@3163
|
225 |
enableFilterButton.image = UIImage(named: "unread-icon-active")
|
andreas@3163
|
226 |
updateFilterText()
|
andreas@3163
|
227 |
} else {
|
andreas@3163
|
228 |
textFilterButton.title = ""
|
andreas@3163
|
229 |
enableFilterButton.image = UIImage(named: "unread-icon")
|
andreas@3163
|
230 |
}
|
andreas@3163
|
231 |
}
|
andreas@3163
|
232 |
|
andreas@3163
|
233 |
func updateFilterText() {
|
andreas@3163
|
234 |
if let vm = model, let txt = vm.activeFilter?.text {
|
andreas@3163
|
235 |
textFilterButton.title = "Filter by: " + txt
|
andreas@3163
|
236 |
}
|
andreas@3151
|
237 |
}
|
dirk@58
|
238 |
|
dirk@31
|
239 |
// MARK: - UITableViewDataSource
|
dirk@31
|
240 |
|
dirk@784
|
241 |
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
andreas@3151
|
242 |
return model?.rowCount ?? 0
|
dirk@31
|
243 |
}
|
dirk@31
|
244 |
|
dirk@784
|
245 |
override func tableView(_ tableView: UITableView,
|
dirk@784
|
246 |
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
andreas@3151
|
247 |
guard let cell = tableView.dequeueReusableCell(withIdentifier: EmailListViewCell.storyboardId,
|
andreas@3151
|
248 |
for: indexPath) as? EmailListViewCell
|
andreas@3151
|
249 |
else {
|
andreas@3151
|
250 |
Log.shared.errorAndCrash(component: #function, errorString: "Wrong cell!")
|
andreas@3151
|
251 |
return UITableViewCell()
|
andreas@3151
|
252 |
}
|
andreas@3151
|
253 |
configure(cell: cell, for: indexPath)
|
dirk@31
|
254 |
return cell
|
dirk@31
|
255 |
}
|
dirk@31
|
256 |
|
dirk@719
|
257 |
// MARK: - UITableViewDelegate
|
dirk@719
|
258 |
|
dirk@784
|
259 |
override func tableView(_ tableView: UITableView, editActionsForRowAt
|
dirk@784
|
260 |
indexPath: IndexPath)-> [UITableViewRowAction]? {
|
andreas@3151
|
261 |
guard let flagAction = createFlagAction(forCellAt: indexPath),
|
andreas@3151
|
262 |
let deleteAction = createDeleteAction(forCellAt: indexPath),
|
andreas@3151
|
263 |
let moreAction = createMoreAction(forCellAt: indexPath) else {
|
andreas@3151
|
264 |
Log.shared.errorAndCrash(component: #function, errorString: "Error creating action.")
|
andreas@3151
|
265 |
return nil
|
dirk@855
|
266 |
}
|
andreas@3151
|
267 |
return [deleteAction, flagAction, moreAction]
|
dirk@744
|
268 |
}
|
dirk@744
|
269 |
|
andreas@3151
|
270 |
override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
andreas@3151
|
271 |
cancelOperation(for: indexPath)
|
dirk@744
|
272 |
}
|
dirk@744
|
273 |
|
andreas@3151
|
274 |
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
andreas@3151
|
275 |
lastSelectedIndexPath = indexPath
|
andreas@3151
|
276 |
tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
|
andreas@3151
|
277 |
performSegue(withIdentifier: SegueIdentifier.segueShowEmail, sender: self)
|
dirk@744
|
278 |
}
|
dirk@744
|
279 |
|
andreas@3151
|
280 |
// MARK: - Queue Handling
|
xavier@1623
|
281 |
|
andreas@3151
|
282 |
private func queue(operation op:Operation, for indexPath: IndexPath) {
|
andreas@3151
|
283 |
operations[indexPath] = op
|
andreas@3151
|
284 |
queue.addOperation(op)
|
dirk@1507
|
285 |
}
|
dirk@1507
|
286 |
|
andreas@3151
|
287 |
private func cancelOperation(for indexPath:IndexPath) {
|
andreas@3151
|
288 |
guard let op = operations.removeValue(forKey: indexPath) else {
|
andreas@3151
|
289 |
return
|
dirk@744
|
290 |
}
|
andreas@3151
|
291 |
if !op.isCancelled {
|
andreas@3151
|
292 |
op.cancel()
|
dirk@1507
|
293 |
}
|
dirk@744
|
294 |
}
|
xavier@1623
|
295 |
|
andreas@3151
|
296 |
override func didReceiveMemoryWarning() {
|
andreas@3151
|
297 |
model?.freeMemory()
|
andreas@3151
|
298 |
}
|
andreas@3151
|
299 |
}
|
andreas@3151
|
300 |
|
andreas@3151
|
301 |
// MARK: - UISearchResultsUpdating, UISearchControllerDelegate
|
andreas@3151
|
302 |
|
andreas@3151
|
303 |
extension EmailListViewController: UISearchResultsUpdating, UISearchControllerDelegate {
|
andreas@3151
|
304 |
public func updateSearchResults(for searchController: UISearchController) {
|
andreas@3151
|
305 |
guard let vm = model, let searchText = searchController.searchBar.text else {
|
andreas@3151
|
306 |
return
|
igor@1242
|
307 |
}
|
andreas@3163
|
308 |
vm.setSearchFilter(forSearchText: searchText)
|
igor@1242
|
309 |
}
|
xavier@1623
|
310 |
|
andreas@3151
|
311 |
func didDismissSearchController(_ searchController: UISearchController) {
|
andreas@3151
|
312 |
guard let vm = model else {
|
andreas@3151
|
313 |
return
|
andreas@3151
|
314 |
}
|
andreas@3151
|
315 |
vm.removeSearchFilter()
|
andreas@3151
|
316 |
}
|
andreas@3151
|
317 |
}
|
xavier@1623
|
318 |
|
andreas@3151
|
319 |
// MARK: - EmailListModelDelegate
|
andreas@3151
|
320 |
|
andreas@3151
|
321 |
extension EmailListViewController: EmailListViewModelDelegate {
|
andreas@3151
|
322 |
func emailListViewModel(viewModel: EmailListViewModel, didInsertDataAt indexPath: IndexPath) {
|
andreas@3166
|
323 |
tableView.beginUpdates()
|
andreas@3151
|
324 |
tableView.insertRows(at: [indexPath], with: .automatic)
|
andreas@3151
|
325 |
tableView.endUpdates()
|
andreas@3151
|
326 |
}
|
andreas@3151
|
327 |
|
andreas@3151
|
328 |
func emailListViewModel(viewModel: EmailListViewModel, didRemoveDataAt indexPath: IndexPath) {
|
andreas@3151
|
329 |
tableView.beginUpdates()
|
andreas@3151
|
330 |
tableView.deleteRows(at: [indexPath], with: .automatic)
|
andreas@3151
|
331 |
tableView.endUpdates()
|
andreas@3151
|
332 |
}
|
andreas@3151
|
333 |
|
andreas@3151
|
334 |
func emailListViewModel(viewModel: EmailListViewModel, didUpdateDataAt indexPath: IndexPath) {
|
andreas@3151
|
335 |
tableView.beginUpdates()
|
andreas@3151
|
336 |
tableView.reloadRows(at: [indexPath], with: .none)
|
andreas@3151
|
337 |
tableView.endUpdates()
|
andreas@3151
|
338 |
}
|
andreas@3151
|
339 |
|
andreas@3151
|
340 |
func updateView() {
|
andreas@3151
|
341 |
self.tableView.reloadData()
|
andreas@3151
|
342 |
}
|
andreas@3151
|
343 |
}
|
andreas@3151
|
344 |
|
andreas@3151
|
345 |
// MARK: - ActionSheet & ActionSheet Actions
|
andreas@3151
|
346 |
|
andreas@3151
|
347 |
extension EmailListViewController {
|
andreas@3166
|
348 |
func showMoreActionSheet(forRowAt indexPath: IndexPath) {
|
andreas@3151
|
349 |
lastSelectedIndexPath = indexPath
|
igor@1242
|
350 |
let alertControler = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
igor@1438
|
351 |
alertControler.view.tintColor = .pEpGreen
|
igor@1242
|
352 |
let cancelAction = createCancelAction()
|
andreas@3151
|
353 |
let replyAction = createReplyAction()
|
andreas@3151
|
354 |
let replyAllAction = createReplyAllAction()
|
andreas@3151
|
355 |
let forwardAction = createForwardAction()
|
igor@1242
|
356 |
alertControler.addAction(cancelAction)
|
igor@1242
|
357 |
alertControler.addAction(replyAction)
|
xavier@1620
|
358 |
alertControler.addAction(replyAllAction)
|
igor@1242
|
359 |
alertControler.addAction(forwardAction)
|
igor@1414
|
360 |
if let popoverPresentationController = alertControler.popoverPresentationController {
|
andreas@3151
|
361 |
popoverPresentationController.sourceView = tableView
|
igor@1414
|
362 |
}
|
igor@1242
|
363 |
present(alertControler, animated: true, completion: nil)
|
igor@1242
|
364 |
}
|
xavier@1623
|
365 |
|
andreas@3151
|
366 |
// MARK: Action Sheet Actions
|
igor@1242
|
367 |
|
igor@1242
|
368 |
func createCancelAction() -> UIAlertAction {
|
andreas@3151
|
369 |
return UIAlertAction(title: "Cancel", style: .cancel) { (action) in
|
andreas@3151
|
370 |
self.tableView.beginUpdates()
|
andreas@3151
|
371 |
self.tableView.setEditing(false, animated: true)
|
andreas@3151
|
372 |
self.tableView.endUpdates()
|
igor@1242
|
373 |
}
|
igor@1242
|
374 |
}
|
xavier@1620
|
375 |
|
andreas@3151
|
376 |
func createReplyAction() -> UIAlertAction {
|
andreas@3151
|
377 |
return UIAlertAction(title: "Reply", style: .default) { (action) in
|
andreas@3151
|
378 |
self.performSegue(withIdentifier: .segueReply, sender: self)
|
xavier@1620
|
379 |
}
|
xavier@1620
|
380 |
}
|
xavier@1620
|
381 |
|
andreas@3151
|
382 |
func createReplyAllAction() -> UIAlertAction {
|
andreas@3151
|
383 |
return UIAlertAction(title: "Reply All", style: .default) { (action) in
|
andreas@3151
|
384 |
self.performSegue(withIdentifier: .segueReplyAll, sender: self)
|
igor@1242
|
385 |
}
|
igor@1242
|
386 |
}
|
xavier@1623
|
387 |
|
andreas@3151
|
388 |
func createForwardAction() -> UIAlertAction {
|
andreas@3151
|
389 |
return UIAlertAction(title: "Forward", style: .default) { (action) in
|
andreas@3151
|
390 |
self.performSegue(withIdentifier: .segueForward, sender: self)
|
xavier@2369
|
391 |
}
|
igor@1301
|
392 |
}
|
igor@1301
|
393 |
}
|
igor@1338
|
394 |
|
andreas@3151
|
395 |
// MARK: - TableViewCell Actions
|
andreas@3151
|
396 |
|
andreas@3151
|
397 |
extension EmailListViewController {
|
andreas@3151
|
398 |
private func createRowAction(image: UIImage?,
|
andreas@3151
|
399 |
action: @escaping (UITableViewRowAction, IndexPath) -> Void,
|
andreas@3151
|
400 |
title: String) -> UITableViewRowAction {
|
andreas@3151
|
401 |
let rowAction = UITableViewRowAction(style: .normal, title: title, handler: action)
|
andreas@3151
|
402 |
if let theImage = image {
|
andreas@3151
|
403 |
let iconColor = UIColor(patternImage: theImage)
|
andreas@3151
|
404 |
rowAction.backgroundColor = iconColor
|
andreas@3151
|
405 |
}
|
andreas@3151
|
406 |
return rowAction
|
andreas@3151
|
407 |
}
|
andreas@3151
|
408 |
|
andreas@3151
|
409 |
func createFlagAction(forCellAt indexPath: IndexPath) -> UITableViewRowAction? {
|
andreas@3151
|
410 |
guard let row = model?.row(for: indexPath) else {
|
andreas@3151
|
411 |
Log.shared.errorAndCrash(component: #function, errorString: "No data for indexPath!")
|
andreas@3151
|
412 |
return nil
|
andreas@3151
|
413 |
}
|
andreas@3151
|
414 |
func action(action: UITableViewRowAction, indexPath: IndexPath) -> Void {
|
andreas@3151
|
415 |
if row.isFlagged {
|
andreas@3151
|
416 |
model?.unsetFlagged(forIndexPath: indexPath)
|
andreas@3151
|
417 |
} else {
|
andreas@3151
|
418 |
model?.setFlagged(forIndexPath: indexPath)
|
andreas@3151
|
419 |
}
|
andreas@3151
|
420 |
tableView.beginUpdates()
|
andreas@3151
|
421 |
tableView.setEditing(false, animated: true)
|
andreas@3166
|
422 |
tableView.reloadRows(at: [indexPath], with: .none)
|
andreas@3151
|
423 |
tableView.endUpdates()
|
andreas@3151
|
424 |
}
|
andreas@3151
|
425 |
let title: String
|
andreas@3151
|
426 |
if row.isFlagged{
|
andreas@3151
|
427 |
let unflagString = NSLocalizedString("Unflag", comment: "Message action (on swipe)")
|
andreas@3151
|
428 |
title = "\n\n\(unflagString)"
|
andreas@3151
|
429 |
} else {
|
andreas@3151
|
430 |
let flagString = NSLocalizedString("Flag", comment: "Message action (on swipe)")
|
andreas@3151
|
431 |
title = "\n\n\(flagString)"
|
andreas@3151
|
432 |
}
|
andreas@3151
|
433 |
return createRowAction(image: UIImage(named: "swipe-flag"), action: action, title: title)
|
andreas@3151
|
434 |
}
|
andreas@3151
|
435 |
|
andreas@3151
|
436 |
func createDeleteAction(forCellAt indexPath: IndexPath) -> UITableViewRowAction? {
|
andreas@3151
|
437 |
func action(action: UITableViewRowAction, indexPath: IndexPath) -> Void {
|
andreas@3151
|
438 |
tableView.beginUpdates()
|
andreas@3151
|
439 |
model?.delete(forIndexPath: indexPath) // mark for deletion/trash
|
andreas@3151
|
440 |
tableView.deleteRows(at: [indexPath], with: .none)
|
andreas@3151
|
441 |
tableView.endUpdates()
|
andreas@3151
|
442 |
}
|
andreas@3151
|
443 |
|
andreas@3151
|
444 |
let title = NSLocalizedString("Delete", comment: "Message action (on swipe)")
|
andreas@3151
|
445 |
return createRowAction(image: UIImage(named: "swipe-trash"), action: action,
|
andreas@3151
|
446 |
title: "\n\n\(title)")
|
andreas@3151
|
447 |
}
|
andreas@3151
|
448 |
|
andreas@3151
|
449 |
func createMoreAction(forCellAt indexPath: IndexPath) -> UITableViewRowAction? {
|
andreas@3151
|
450 |
func action(action: UITableViewRowAction, indexPath: IndexPath) -> Void {
|
andreas@3151
|
451 |
self.showMoreActionSheet(forRowAt: indexPath)
|
andreas@3151
|
452 |
}
|
andreas@3151
|
453 |
|
andreas@3151
|
454 |
let title = NSLocalizedString("More", comment: "Message action (on swipe)")
|
andreas@3151
|
455 |
return createRowAction(image: UIImage(named: "swipe-more"),
|
andreas@3151
|
456 |
action: action,
|
andreas@3151
|
457 |
title: "\n\n\(title)")
|
andreas@3151
|
458 |
}
|
andreas@3151
|
459 |
}
|
andreas@3151
|
460 |
|
andreas@3151
|
461 |
// MARK: - SegueHandlerType
|
igor@1338
|
462 |
|
igor@1338
|
463 |
extension EmailListViewController: SegueHandlerType {
|
xavier@1623
|
464 |
|
igor@1338
|
465 |
enum SegueIdentifier: String {
|
igor@1338
|
466 |
case segueAddNewAccount
|
igor@1338
|
467 |
case segueShowEmail
|
igor@1338
|
468 |
case segueCompose
|
xavier@2715
|
469 |
case segueReply
|
xavier@1623
|
470 |
case segueReplyAll
|
xavier@1664
|
471 |
case segueForward
|
xavier@1865
|
472 |
case segueFilter
|
xavier@2248
|
473 |
case segueFolderViews
|
igor@1338
|
474 |
case noSegue
|
igor@1338
|
475 |
}
|
xavier@1623
|
476 |
|
andreas@3110
|
477 |
private func setup(composeViewController vc: ComposeTableViewController,
|
andreas@3110
|
478 |
composeMode: ComposeTableViewController.ComposeMode = .normal,
|
andreas@3110
|
479 |
originalMessage: Message? = nil) {
|
andreas@3110
|
480 |
vc.appConfig = appConfig
|
andreas@3110
|
481 |
vc.composeMode = composeMode
|
andreas@3110
|
482 |
vc.originalMessage = originalMessage
|
andreas@3151
|
483 |
vc.origin = model?.folderToShow?.account.user
|
andreas@3110
|
484 |
}
|
andreas@3110
|
485 |
|
igor@1338
|
486 |
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
igor@1338
|
487 |
switch segueIdentifier(for: segue) {
|
xavier@2715
|
488 |
case .segueReply:
|
andreas@2850
|
489 |
guard let nav = segue.destination as? UINavigationController,
|
xavier@2715
|
490 |
let destination = nav.topViewController as? ComposeTableViewController,
|
andreas@3151
|
491 |
let indexPath = lastSelectedIndexPath,
|
andreas@3151
|
492 |
let message = model?.message(representedByRowAt: indexPath) else {
|
andreas@2850
|
493 |
Log.shared.errorAndCrash(component: #function, errorString: "Segue issue")
|
andreas@2850
|
494 |
return
|
xavier@2715
|
495 |
}
|
andreas@3110
|
496 |
setup(composeViewController: destination, composeMode: .replyFrom,
|
andreas@3151
|
497 |
originalMessage: message)
|
xavier@1623
|
498 |
case .segueReplyAll:
|
andreas@2850
|
499 |
guard let nav = segue.destination as? UINavigationController,
|
xavier@1623
|
500 |
let destination = nav.topViewController as? ComposeTableViewController,
|
andreas@3151
|
501 |
let indexPath = lastSelectedIndexPath,
|
andreas@3151
|
502 |
let message = model?.message(representedByRowAt: indexPath) else {
|
andreas@2850
|
503 |
Log.shared.errorAndCrash(component: #function, errorString: "Segue issue")
|
andreas@2850
|
504 |
return
|
xavier@1623
|
505 |
}
|
andreas@3110
|
506 |
setup(composeViewController: destination, composeMode: .replyAll,
|
andreas@3151
|
507 |
originalMessage: message)
|
andreas@3111
|
508 |
case .segueShowEmail:
|
andreas@2850
|
509 |
guard let vc = segue.destination as? EmailViewController,
|
andreas@3151
|
510 |
let indexPath = lastSelectedIndexPath,
|
andreas@3166
|
511 |
let message = model?.message(representedByRowAt: indexPath) else { //BUFF: maybe remove message(representedByRowAt: and handle in dvc.
|
andreas@2850
|
512 |
Log.shared.errorAndCrash(component: #function, errorString: "Segue issue")
|
andreas@2850
|
513 |
return
|
igor@1338
|
514 |
}
|
andreas@2850
|
515 |
vc.appConfig = appConfig
|
andreas@3151
|
516 |
vc.message = message
|
andreas@3151
|
517 |
vc.folderShow = model?.folderToShow
|
andreas@3166
|
518 |
vc.messageId = indexPath.row //that looks wrong
|
xavier@1664
|
519 |
case .segueForward:
|
andreas@2850
|
520 |
guard let nav = segue.destination as? UINavigationController,
|
xavier@1664
|
521 |
let destination = nav.topViewController as? ComposeTableViewController,
|
andreas@3151
|
522 |
let indexPath = lastSelectedIndexPath,
|
andreas@3151
|
523 |
let message = model?.message(representedByRowAt: indexPath) else {
|
andreas@2850
|
524 |
Log.shared.errorAndCrash(component: #function, errorString: "Segue issue")
|
andreas@2850
|
525 |
return
|
xavier@1664
|
526 |
}
|
andreas@3110
|
527 |
setup(composeViewController: destination, composeMode: .forward,
|
andreas@3151
|
528 |
originalMessage: message)
|
andreas@2850
|
529 |
case .segueFilter:
|
andreas@2850
|
530 |
guard let destiny = segue.destination as? FilterTableViewController else {
|
andreas@2850
|
531 |
Log.shared.errorAndCrash(component: #function, errorString: "Segue issue")
|
andreas@2850
|
532 |
return
|
andreas@2850
|
533 |
}
|
andreas@2850
|
534 |
destiny.appConfig = appConfig
|
andreas@3163
|
535 |
destiny.filterDelegate = model
|
andreas@2850
|
536 |
destiny.inFolder = false
|
andreas@3165
|
537 |
destiny.filterEnabled = model?.folderToShow?.filter?.clone()
|
andreas@2850
|
538 |
destiny.hidesBottomBarWhenPushed = true
|
andreas@2850
|
539 |
case .segueAddNewAccount:
|
andreas@2850
|
540 |
guard let vc = segue.destination as? LoginTableViewController else {
|
andreas@2850
|
541 |
Log.shared.errorAndCrash(component: #function, errorString: "Segue issue")
|
andreas@2850
|
542 |
return
|
andreas@2850
|
543 |
}
|
andreas@2850
|
544 |
vc.appConfig = appConfig
|
andreas@2850
|
545 |
vc.hidesBottomBarWhenPushed = true
|
xavier@1664
|
546 |
break
|
andreas@2850
|
547 |
case .segueFolderViews:
|
andreas@2850
|
548 |
guard let vC = segue.destination as? FolderTableViewController else {
|
andreas@2850
|
549 |
Log.shared.errorAndCrash(component: #function, errorString: "Segue issue")
|
andreas@2850
|
550 |
return
|
xavier@1865
|
551 |
}
|
andreas@2850
|
552 |
vC.appConfig = appConfig
|
andreas@2850
|
553 |
vC.hidesBottomBarWhenPushed = true
|
xavier@1865
|
554 |
break
|
andreas@2850
|
555 |
case .segueCompose:
|
andreas@2850
|
556 |
guard let nav = segue.destination as? UINavigationController,
|
dirk@2957
|
557 |
let destination = nav.rootViewController as? ComposeTableViewController else {
|
andreas@2850
|
558 |
Log.shared.errorAndCrash(component: #function, errorString: "Segue issue")
|
andreas@2850
|
559 |
return
|
dirk@2240
|
560 |
}
|
andreas@3110
|
561 |
setup(composeViewController: destination)
|
andreas@2850
|
562 |
default:
|
andreas@2850
|
563 |
Log.shared.errorAndCrash(component: #function, errorString: "Unhandled segue")
|
dirk@1694
|
564 |
break
|
igor@1338
|
565 |
}
|
andreas@2838
|
566 |
}
|
xavier@1865
|
567 |
|
andreas@3166
|
568 |
@IBAction func segueUnwindAccountAdded(segue: UIStoryboardSegue) {
|
andreas@3151
|
569 |
// nothing to do.
|
andreas@3150
|
570 |
}
|
dirk@1348
|
571 |
}
|