ChatListController.swift 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. import UIKit
  2. class ChatListController: UIViewController {
  3. weak var coordinator: ChatListCoordinator?
  4. private var dcContext: DcContext
  5. private var chatList: DcChatlist?
  6. private var showArchive: Bool
  7. private lazy var chatTable: UITableView = {
  8. let chatTable = UITableView()
  9. chatTable.dataSource = self
  10. chatTable.delegate = self
  11. chatTable.rowHeight = 80
  12. return chatTable
  13. }()
  14. private var msgChangedObserver: Any?
  15. private var incomingMsgObserver: Any?
  16. private var viewChatObserver: Any?
  17. private var newButton: UIBarButtonItem!
  18. init(dcContext: DcContext, showArchive: Bool) {
  19. self.dcContext = dcContext
  20. self.showArchive = showArchive
  21. super.init(nibName: nil, bundle: nil)
  22. }
  23. required init?(coder _: NSCoder) {
  24. fatalError("init(coder:) has not been implemented")
  25. }
  26. override func viewWillAppear(_ animated: Bool) {
  27. super.viewWillAppear(animated)
  28. getChatList()
  29. if RelayHelper.sharedInstance.isForwarding() {
  30. title = String.localized("forward_to")
  31. } else {
  32. title = showArchive ? String.localized("chat_archived_chats_title") :
  33. String.localized("pref_chats")
  34. }
  35. }
  36. override func viewDidAppear(_ animated: Bool) {
  37. super.viewDidAppear(animated)
  38. let nc = NotificationCenter.default
  39. msgChangedObserver = nc.addObserver(forName: dcNotificationChanged,
  40. object: nil, queue: nil) { _ in
  41. self.getChatList()
  42. }
  43. incomingMsgObserver = nc.addObserver(forName: dcNotificationIncoming,
  44. object: nil, queue: nil) { _ in
  45. self.getChatList()
  46. }
  47. viewChatObserver = nc.addObserver(forName: dcNotificationViewChat, object: nil, queue: nil) { notification in
  48. if let chatId = notification.userInfo?["chat_id"] as? Int {
  49. self.coordinator?.showChat(chatId: chatId)
  50. }
  51. }
  52. }
  53. override func viewDidDisappear(_ animated: Bool) {
  54. super.viewDidDisappear(animated)
  55. let nc = NotificationCenter.default
  56. if let msgChangedObserver = self.msgChangedObserver {
  57. nc.removeObserver(msgChangedObserver)
  58. }
  59. if let incomingMsgObserver = self.incomingMsgObserver {
  60. nc.removeObserver(incomingMsgObserver)
  61. }
  62. if let viewChatObserver = self.viewChatObserver {
  63. nc.removeObserver(viewChatObserver)
  64. }
  65. }
  66. override func viewDidLoad() {
  67. super.viewDidLoad()
  68. newButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.compose, target: self, action: #selector(didPressNewChat))
  69. newButton.tintColor = DcColors.primary
  70. navigationItem.rightBarButtonItem = newButton
  71. setupChatTable()
  72. }
  73. private func setupChatTable() {
  74. view.addSubview(chatTable)
  75. chatTable.translatesAutoresizingMaskIntoConstraints = false
  76. chatTable.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
  77. chatTable.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
  78. chatTable.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
  79. chatTable.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
  80. }
  81. @objc func didPressNewChat() {
  82. coordinator?.showNewChatController()
  83. }
  84. private func getNumberOfArchivedChats() -> Int {
  85. let chatList = dcContext.getChatlist(flags: DC_GCL_ARCHIVED_ONLY, queryString: nil, queryId: 0)
  86. return chatList.length
  87. }
  88. private func getChatList() {
  89. var gclFlags: Int32 = 0
  90. if showArchive {
  91. gclFlags |= DC_GCL_ARCHIVED_ONLY
  92. }
  93. chatList = dcContext.getChatlist(flags: gclFlags, queryString: nil, queryId: 0)
  94. chatTable.reloadData()
  95. }
  96. }
  97. extension ChatListController: UITableViewDataSource, UITableViewDelegate {
  98. func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
  99. guard let chatList = self.chatList else {
  100. fatalError("chatList was nil in data source")
  101. }
  102. return chatList.length
  103. }
  104. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  105. let row = indexPath.row
  106. guard let chatList = self.chatList else {
  107. fatalError("chatList was nil in data source")
  108. }
  109. let chatId = chatList.getChatId(index: row)
  110. if chatId == DC_CHAT_ID_ARCHIVED_LINK {
  111. return getArchiveCell(tableView)
  112. }
  113. let cell: ContactCell
  114. if let c = tableView.dequeueReusableCell(withIdentifier: "ChatCell") as? ContactCell {
  115. cell = c
  116. } else {
  117. cell = ContactCell(style: .default, reuseIdentifier: "ChatCell")
  118. }
  119. let chat = DcChat(id: chatId)
  120. let summary = chatList.getSummary(index: row)
  121. let unreadMessages = dcContext.getUnreadMessages(chatId: chatId)
  122. cell.nameLabel.attributedText = (unreadMessages > 0) ?
  123. NSAttributedString(string: chat.name, attributes: [ .font: UIFont.systemFont(ofSize: 16, weight: .bold) ]) :
  124. NSAttributedString(string: chat.name, attributes: [ .font: UIFont.systemFont(ofSize: 16, weight: .medium) ])
  125. if let img = chat.profileImage {
  126. cell.resetBackupImage()
  127. cell.setImage(img)
  128. } else {
  129. cell.setBackupImage(name: chat.name, color: chat.color)
  130. }
  131. cell.setVerified(isVerified: chat.isVerified)
  132. let result1 = summary.text1 ?? ""
  133. let result2 = summary.text2 ?? ""
  134. let result: String
  135. if !result1.isEmpty, !result2.isEmpty {
  136. result = "\(result1): \(result2)"
  137. } else {
  138. result = "\(result1)\(result2)"
  139. }
  140. cell.emailLabel.text = result
  141. cell.setTimeLabel(summary.timestamp)
  142. cell.setUnreadMessageCounter(unreadMessages)
  143. cell.setDeliveryStatusIndicator(summary.state)
  144. return cell
  145. }
  146. func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) {
  147. let row = indexPath.row
  148. guard let chatList = chatList else { return }
  149. let chatId = chatList.getChatId(index: row)
  150. if chatId == DC_CHAT_ID_DEADDROP {
  151. let msgId = chatList.getMsgId(index: row)
  152. let dcMsg = DcMsg(id: msgId)
  153. let dcContact = DcContact(id: dcMsg.fromContactId)
  154. let title = String.localizedStringWithFormat(String.localized("ask_start_chat_with"), dcContact.nameNAddr)
  155. let alert = UIAlertController(title: title, message: nil, preferredStyle: .actionSheet)
  156. alert.addAction(UIAlertAction(title: String.localized("start_chat"), style: .default, handler: { _ in
  157. let chat = dcMsg.createChat()
  158. self.coordinator?.showChat(chatId: chat.id)
  159. }))
  160. alert.addAction(UIAlertAction(title: String.localized("not_now"), style: .default, handler: { _ in
  161. dcContact.marknoticed()
  162. }))
  163. alert.addAction(UIAlertAction(title: String.localized("menu_block_contact"), style: .destructive, handler: { _ in
  164. dcContact.block()
  165. }))
  166. alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel))
  167. present(alert, animated: true, completion: nil)
  168. } else if chatId == DC_CHAT_ID_ARCHIVED_LINK {
  169. coordinator?.showArchive()
  170. } else {
  171. coordinator?.showChat(chatId: chatId)
  172. }
  173. }
  174. func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
  175. let row = indexPath.row
  176. guard let chatList = chatList else {
  177. return []
  178. }
  179. let chatId = chatList.getChatId(index: row)
  180. if chatId==DC_CHAT_ID_ARCHIVED_LINK {
  181. return []
  182. // returning nil may result in a default delete action,
  183. // see https://forums.developer.apple.com/thread/115030
  184. }
  185. var title = String.localized("menu_archive_chat")
  186. if showArchive {
  187. title = String.localized("menu_unarchive_chat")
  188. }
  189. let archive = UITableViewRowAction(style: .destructive, title: title) { [unowned self] _, _ in
  190. self.dcContext.archiveChat(chatId: chatId, archive: !self.showArchive)
  191. }
  192. archive.backgroundColor = UIColor.lightGray
  193. let delete = UITableViewRowAction(style: .destructive, title: String.localized("menu_delete_chat")) { [unowned self] _, _ in
  194. self.showDeleteChatConfirmationAlert(chatId: chatId)
  195. }
  196. delete.backgroundColor = UIColor.red
  197. return [archive, delete]
  198. }
  199. func getArchiveCell(_ tableView: UITableView) -> UITableViewCell {
  200. let archiveCell: UITableViewCell
  201. if let cell = tableView.dequeueReusableCell(withIdentifier: "ArchiveCell") {
  202. archiveCell = cell
  203. } else {
  204. archiveCell = UITableViewCell(style: .default, reuseIdentifier: "ArchiveCell")
  205. }
  206. archiveCell.textLabel?.textAlignment = .center
  207. var title = String.localized("chat_archived_chats_title")
  208. let count = getNumberOfArchivedChats()
  209. title.append(" (\(count))")
  210. archiveCell.textLabel?.text = title
  211. archiveCell.textLabel?.textColor = .systemBlue
  212. return archiveCell
  213. }
  214. private func showDeleteChatConfirmationAlert(chatId: Int) {
  215. let alert = UIAlertController(
  216. title: nil,
  217. message: String.localized("ask_delete_chat_desktop"),
  218. preferredStyle: .actionSheet
  219. )
  220. alert.addAction(UIAlertAction(title: String.localized("menu_delete_chat"), style: .destructive, handler: { _ in
  221. self.dcContext.deleteChat(chatId: chatId)
  222. self.getChatList()
  223. }))
  224. alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
  225. self.present(alert, animated: true, completion: nil)
  226. }
  227. }