|
@@ -2,8 +2,9 @@ import UIKit
|
|
|
import DcCore
|
|
|
|
|
|
class ChatListController: UITableViewController {
|
|
|
- let viewModel: ChatListViewModel
|
|
|
+ var viewModel: ChatListViewModel?
|
|
|
let dcContext: DcContext
|
|
|
+ var isArchive: Bool
|
|
|
|
|
|
private let chatCellReuseIdentifier = "chat_cell"
|
|
|
private let deadDropCellReuseIdentifier = "deaddrop_cell"
|
|
@@ -59,15 +60,18 @@ class ChatListController: UITableViewController {
|
|
|
return label
|
|
|
}()
|
|
|
|
|
|
- init(dcContext: DcContext, viewModel: ChatListViewModel) {
|
|
|
- self.viewModel = viewModel
|
|
|
+ init(dcContext: DcContext, isArchive: Bool) {
|
|
|
self.dcContext = dcContext
|
|
|
- if viewModel.isArchive {
|
|
|
- super.init(style: .grouped)
|
|
|
- } else {
|
|
|
- super.init(style: .grouped)
|
|
|
+ self.isArchive = isArchive
|
|
|
+ super.init(style: .grouped)
|
|
|
+ DispatchQueue.global(qos: .userInteractive).async { [weak self] in
|
|
|
+ guard let self = self else { return }
|
|
|
+ self.viewModel = ChatListViewModel(dcContext: self.dcContext, isArchive: isArchive)
|
|
|
+ self.viewModel?.onChatListUpdate = self.handleChatListUpdate
|
|
|
+ DispatchQueue.main.async { [weak self] in
|
|
|
+ self?.tableView.reloadData()
|
|
|
+ }
|
|
|
}
|
|
|
- viewModel.onChatListUpdate = handleChatListUpdate // register listener
|
|
|
}
|
|
|
|
|
|
required init?(coder _: NSCoder) {
|
|
@@ -77,7 +81,7 @@ class ChatListController: UITableViewController {
|
|
|
// MARK: - lifecycle
|
|
|
override func viewDidLoad() {
|
|
|
super.viewDidLoad()
|
|
|
- if !viewModel.isArchive {
|
|
|
+ if isArchive {
|
|
|
navigationItem.rightBarButtonItem = newButton
|
|
|
navigationItem.searchController = searchController
|
|
|
}
|
|
@@ -142,9 +146,10 @@ class ChatListController: UITableViewController {
|
|
|
queue: nil) { [weak self] _ in
|
|
|
guard let self = self else { return }
|
|
|
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
|
|
|
- self.viewModel.searchActive,
|
|
|
+ let viewModel = self.viewModel,
|
|
|
+ viewModel.searchActive,
|
|
|
appDelegate.appIsInForeground() {
|
|
|
- self.viewModel.updateSearchResults(for: self.searchController)
|
|
|
+ viewModel.updateSearchResults(for: self.searchController)
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -222,7 +227,7 @@ class ChatListController: UITableViewController {
|
|
|
|
|
|
private func setupSubviews() {
|
|
|
emptyStateLabel.addCenteredTo(parentView: view)
|
|
|
- navigationItem.backButtonTitle = viewModel.isArchive ? String.localized("chat_archived_chats_title") : String.localized("pref_chats")
|
|
|
+ navigationItem.backButtonTitle = isArchive ? String.localized("chat_archived_chats_title") : String.localized("pref_chats")
|
|
|
}
|
|
|
|
|
|
@objc
|
|
@@ -240,11 +245,14 @@ class ChatListController: UITableViewController {
|
|
|
tableView.rowHeight = ContactCell.cellHeight
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+ private var isInitial = true
|
|
|
@objc func applicationDidBecomeActive(_ notification: NSNotification) {
|
|
|
if navigationController?.visibleViewController == self {
|
|
|
- startTimer()
|
|
|
- refreshInBg()
|
|
|
+ if !isInitial {
|
|
|
+ startTimer()
|
|
|
+ refreshInBg()
|
|
|
+ isInitial = false
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -259,13 +267,13 @@ class ChatListController: UITableViewController {
|
|
|
// do at least one refresh, without inital delay
|
|
|
// (refreshData() calls handleChatListUpdate() on main thread when done)
|
|
|
self?.needsAnotherBgRefresh = false
|
|
|
- self?.viewModel.refreshData()
|
|
|
+ self?.viewModel?.refreshData()
|
|
|
|
|
|
// do subsequent refreshes with a delay of 500ms
|
|
|
while self?.needsAnotherBgRefresh != false {
|
|
|
usleep(500000)
|
|
|
self?.needsAnotherBgRefresh = false
|
|
|
- self?.viewModel.refreshData()
|
|
|
+ self?.viewModel?.refreshData()
|
|
|
}
|
|
|
|
|
|
self?.inBgRefresh = false
|
|
@@ -299,7 +307,7 @@ class ChatListController: UITableViewController {
|
|
|
}
|
|
|
private func quitSearch(animated: Bool) {
|
|
|
searchController.searchBar.text = nil
|
|
|
- self.viewModel.endSearch()
|
|
|
+ self.viewModel?.endSearch()
|
|
|
searchController.dismiss(animated: animated) {
|
|
|
self.tableView.scrollToTop()
|
|
|
}
|
|
@@ -309,17 +317,18 @@ class ChatListController: UITableViewController {
|
|
|
|
|
|
|
|
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
|
|
- return viewModel.numberOfSections
|
|
|
+ return viewModel?.numberOfSections ?? 0
|
|
|
}
|
|
|
|
|
|
override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
|
- return viewModel.numberOfRowsIn(section: section)
|
|
|
+ return viewModel?.numberOfRowsIn(section: section) ?? 0
|
|
|
}
|
|
|
|
|
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
|
-
|
|
|
+ guard let viewModel = viewModel else {
|
|
|
+ return UITableViewCell()
|
|
|
+ }
|
|
|
let cellData = viewModel.cellDataFor(section: indexPath.section, row: indexPath.row)
|
|
|
-
|
|
|
switch cellData.type {
|
|
|
case .chat(let chatData):
|
|
|
let chatId = chatData.chatId
|
|
@@ -340,16 +349,23 @@ class ChatListController: UITableViewController {
|
|
|
}
|
|
|
case .profile:
|
|
|
safe_fatalError("CellData type profile not allowed")
|
|
|
+ default:
|
|
|
+ break
|
|
|
}
|
|
|
safe_fatalError("Could not find/dequeue or recycle UITableViewCell.")
|
|
|
return UITableViewCell()
|
|
|
}
|
|
|
|
|
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
|
|
- return viewModel.titleForHeaderIn(section: section)
|
|
|
+ return viewModel?.titleForHeaderIn(section: section)
|
|
|
}
|
|
|
|
|
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
|
+ guard let viewModel = viewModel else {
|
|
|
+ tableView.deselectRow(at: indexPath, animated: false)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
let cellData = viewModel.cellDataFor(section: indexPath.section, row: indexPath.row)
|
|
|
switch cellData.type {
|
|
|
case .chat(let chatData):
|
|
@@ -373,6 +389,7 @@ class ChatListController: UITableViewController {
|
|
|
}
|
|
|
|
|
|
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
|
|
|
+ guard let viewModel = viewModel else { return [] }
|
|
|
|
|
|
guard let chatId = viewModel.chatIdFor(section: indexPath.section, row: indexPath.row) else {
|
|
|
return []
|
|
@@ -388,13 +405,13 @@ class ChatListController: UITableViewController {
|
|
|
let archiveActionTitle: String = String.localized(archived ? "unarchive" : "archive")
|
|
|
|
|
|
let archiveAction = UITableViewRowAction(style: .destructive, title: archiveActionTitle) { [weak self] _, _ in
|
|
|
- self?.viewModel.archiveChatToggle(chatId: chatId)
|
|
|
+ self?.viewModel?.archiveChatToggle(chatId: chatId)
|
|
|
}
|
|
|
archiveAction.backgroundColor = UIColor.lightGray
|
|
|
|
|
|
let pinned = chat.visibility==DC_CHAT_VISIBILITY_PINNED
|
|
|
let pinAction = UITableViewRowAction(style: .destructive, title: String.localized(pinned ? "unpin" : "pin")) { [weak self] _, _ in
|
|
|
- self?.viewModel.pinChatToggle(chatId: chat.id)
|
|
|
+ self?.viewModel?.pinChatToggle(chatId: chat.id)
|
|
|
}
|
|
|
pinAction.backgroundColor = UIColor.systemGreen
|
|
|
|
|
@@ -410,10 +427,10 @@ class ChatListController: UITableViewController {
|
|
|
private func updateTitle() {
|
|
|
if RelayHelper.sharedInstance.isForwarding() {
|
|
|
titleView.text = String.localized("forward_to")
|
|
|
- if !viewModel.isArchive {
|
|
|
+ if isArchive {
|
|
|
navigationItem.setLeftBarButton(cancelButton, animated: true)
|
|
|
}
|
|
|
- } else if viewModel.isArchive {
|
|
|
+ } else if isArchive {
|
|
|
titleView.text = String.localized("chat_archived_chats_title")
|
|
|
navigationItem.setLeftBarButton(nil, animated: true)
|
|
|
|
|
@@ -430,14 +447,14 @@ class ChatListController: UITableViewController {
|
|
|
}
|
|
|
|
|
|
private func handleEmptyStateLabel() {
|
|
|
- if let emptySearchText = viewModel.emptySearchText {
|
|
|
+ if let emptySearchText = viewModel?.emptySearchText {
|
|
|
let text = String.localizedStringWithFormat(
|
|
|
String.localized("search_no_result_for_x"),
|
|
|
emptySearchText
|
|
|
)
|
|
|
emptyStateLabel.text = text
|
|
|
emptyStateLabel.isHidden = false
|
|
|
- } else if viewModel.isArchive && viewModel.numberOfRowsIn(section: 0) == 0 {
|
|
|
+ } else if isArchive && (viewModel?.numberOfRowsIn(section: 0) ?? 0) == 0 {
|
|
|
emptyStateLabel.text = String.localized("archive_empty_hint")
|
|
|
emptyStateLabel.isHidden = false
|
|
|
} else {
|
|
@@ -533,6 +550,7 @@ class ChatListController: UITableViewController {
|
|
|
}
|
|
|
|
|
|
private func deleteChat(chatId: Int, animated: Bool) {
|
|
|
+ guard let viewModel = viewModel else { return }
|
|
|
if !animated {
|
|
|
viewModel.deleteChat(chatId: chatId)
|
|
|
refreshInBg()
|
|
@@ -564,8 +582,7 @@ class ChatListController: UITableViewController {
|
|
|
}
|
|
|
|
|
|
public func showArchive(animated: Bool) {
|
|
|
- let viewModel = ChatListViewModel(dcContext: dcContext, isArchive: true)
|
|
|
- let controller = ChatListController(dcContext: dcContext, viewModel: viewModel)
|
|
|
+ let controller = ChatListController(dcContext: dcContext, isArchive: true)
|
|
|
navigationController?.pushViewController(controller, animated: animated)
|
|
|
}
|
|
|
|
|
@@ -578,13 +595,13 @@ class ChatListController: UITableViewController {
|
|
|
// MARK: - uisearchbardelegate
|
|
|
extension ChatListController: UISearchBarDelegate {
|
|
|
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
|
|
|
- viewModel.beginSearch()
|
|
|
+ viewModel?.beginSearch()
|
|
|
return true
|
|
|
}
|
|
|
|
|
|
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
|
|
// searchBar will be set to "" by system
|
|
|
- viewModel.endSearch()
|
|
|
+ viewModel?.endSearch()
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
|
|
self.tableView.scrollToTop()
|
|
|
}
|