ChatListViewModel.swift 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import UIKit
  2. import DcCore
  3. // MARK: - ChatListViewModel
  4. class ChatListViewModel: NSObject {
  5. var onChatListUpdate: VoidFunction?
  6. enum ChatListSectionType {
  7. case chats
  8. case contacts
  9. }
  10. private let dcContext: DcContext
  11. var searchActive: Bool = false
  12. // if searchfield is empty we show default chat list
  13. private var showSearchResults: Bool {
  14. return searchActive && searchText.containsCharacters()
  15. }
  16. private var chatList: DcChatlist!
  17. // for search filtering
  18. private var searchText: String = ""
  19. private var searchResultChatList: DcChatlist?
  20. private var searchResultContactIds: [Int] = []
  21. // to manage sections dynamically
  22. private var searchResultsChatsSection: ChatListSectionType = .chats
  23. private var searchResultsContactsSection: ChatListSectionType = .contacts
  24. private var searchResultSections: [ChatListSectionType] = []
  25. init(dcContext: DcContext) {
  26. self.dcContext = dcContext
  27. super.init()
  28. updateChatList(notifyListener: true)
  29. }
  30. private func updateChatList(notifyListener: Bool) {
  31. self.chatList = dcContext.getChatlist(flags: DC_GCL_ADD_ALLDONE_HINT | DC_GCL_FOR_FORWARDING | DC_GCL_NO_SPECIALS, queryString: nil, queryId: 0)
  32. if notifyListener {
  33. onChatListUpdate?()
  34. }
  35. }
  36. func getChatId(section: Int, row: Int) -> Int? {
  37. if showSearchResults {
  38. switch searchResultSections[section] {
  39. case .chats:
  40. let list: DcChatlist? = searchResultChatList
  41. return list?.getChatId(index: row)
  42. case .contacts:
  43. return searchResultContactIds[row]
  44. }
  45. }
  46. return chatList.getChatId(index: row)
  47. }
  48. func cellDataFor(section: Int, row: Int) -> AvatarCellViewModel {
  49. if showSearchResults {
  50. switch searchResultSections[section] {
  51. case .chats:
  52. return makeChatCellViewModel(index: row, searchText: searchText)
  53. case .contacts:
  54. return ContactCellViewModel.make(contactId: searchResultContactIds[row], searchText: searchText, dcContext: dcContext)
  55. }
  56. }
  57. return makeChatCellViewModel(index: row, searchText: "")
  58. }
  59. func makeChatCellViewModel(index: Int, searchText: String) -> AvatarCellViewModel {
  60. let list: DcChatlist = searchResultChatList ?? chatList
  61. let chatId = list.getChatId(index: index)
  62. let summary = list.getSummary(index: index)
  63. let chat = dcContext.getChat(chatId: chatId)
  64. let unreadMessages = dcContext.getUnreadMessages(chatId: chatId)
  65. var chatTitleIndexes: [Int] = []
  66. if searchText.containsCharacters() {
  67. let chatName = chat.name
  68. chatTitleIndexes = chatName.containsExact(subSequence: searchText)
  69. }
  70. let viewModel = ChatCellViewModel(
  71. dcContext: dcContext,
  72. chatData: ChatCellData(
  73. chatId: chatId,
  74. summary: summary,
  75. unreadMessages: unreadMessages
  76. ),
  77. titleHighlightIndexes: chatTitleIndexes
  78. )
  79. return viewModel
  80. }
  81. var numberOfSections: Int {
  82. if showSearchResults {
  83. return searchResultSections.count
  84. }
  85. return 1
  86. }
  87. func numberOfRowsIn(section: Int) -> Int {
  88. if showSearchResults {
  89. switch searchResultSections[section] {
  90. case .chats:
  91. return searchResultChatList?.length ?? 0
  92. case .contacts:
  93. return searchResultContactIds.count
  94. }
  95. }
  96. return chatList.length
  97. }
  98. func titleForHeaderIn(section: Int) -> String? {
  99. if showSearchResults {
  100. let title: String
  101. switch searchResultSections[section] {
  102. case .chats:
  103. title = "n_chats"
  104. case .contacts:
  105. title = "n_contacts"
  106. }
  107. return String.localized(stringID: title, count: numberOfRowsIn(section: section))
  108. }
  109. return nil
  110. }
  111. func refreshData() {
  112. updateChatList(notifyListener: true)
  113. }
  114. func beginSearch() {
  115. searchActive = true
  116. }
  117. func endSearch() {
  118. searchActive = false
  119. searchText = ""
  120. resetSearch()
  121. }
  122. var emptySearchText: String? {
  123. if searchActive && numberOfSections == 0 {
  124. return searchText
  125. }
  126. return nil
  127. }
  128. // MARK: - search
  129. func updateSearchResultSections() {
  130. var sections: [ChatListSectionType] = []
  131. if let chatList = searchResultChatList, chatList.length > 0 {
  132. sections.append(searchResultsChatsSection)
  133. }
  134. if !searchResultContactIds.isEmpty {
  135. sections.append(searchResultsContactsSection)
  136. }
  137. searchResultSections = sections
  138. }
  139. func resetSearch() {
  140. searchResultChatList = nil
  141. searchResultContactIds = []
  142. updateSearchResultSections()
  143. }
  144. func filterContentForSearchText(_ searchText: String) {
  145. if !searchText.isEmpty {
  146. filterAndUpdateList(searchText: searchText)
  147. } else {
  148. // when search input field empty we show default chatList
  149. resetSearch()
  150. }
  151. onChatListUpdate?()
  152. }
  153. func filterAndUpdateList(searchText: String) {
  154. // #1 chats with searchPattern in title bar
  155. var flags: Int32 = 0
  156. flags |= DC_GCL_NO_SPECIALS
  157. searchResultChatList = dcContext.getChatlist(flags: flags, queryString: searchText, queryId: 0)
  158. // #2 contacts with searchPattern in name or in email
  159. searchResultContactIds = dcContext.getContacts(flags: DC_GCL_ADD_SELF, queryString: searchText)
  160. updateSearchResultSections()
  161. }
  162. }
  163. // MARK: UISearchResultUpdating
  164. extension ChatListViewModel: UISearchResultsUpdating {
  165. func updateSearchResults(for searchController: UISearchController) {
  166. self.searchText = searchController.searchBar.text ?? ""
  167. if let searchText = searchController.searchBar.text {
  168. filterContentForSearchText(searchText)
  169. return
  170. }
  171. }
  172. }