ChatListViewModel.swift 6.2 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. highlightMsgId: nil,
  75. summary: summary,
  76. unreadMessages: unreadMessages
  77. ),
  78. titleHighlightIndexes: chatTitleIndexes
  79. )
  80. return viewModel
  81. }
  82. var numberOfSections: Int {
  83. if showSearchResults {
  84. return searchResultSections.count
  85. }
  86. return 1
  87. }
  88. func numberOfRowsIn(section: Int) -> Int {
  89. if showSearchResults {
  90. switch searchResultSections[section] {
  91. case .chats:
  92. return searchResultChatList?.length ?? 0
  93. case .contacts:
  94. return searchResultContactIds.count
  95. }
  96. }
  97. return chatList.length
  98. }
  99. func titleForHeaderIn(section: Int) -> String? {
  100. if showSearchResults {
  101. let title: String
  102. switch searchResultSections[section] {
  103. case .chats:
  104. title = "n_chats"
  105. case .contacts:
  106. title = "n_contacts"
  107. }
  108. return String.localized(stringID: title, count: numberOfRowsIn(section: section))
  109. }
  110. return nil
  111. }
  112. func refreshData() {
  113. updateChatList(notifyListener: true)
  114. }
  115. func beginSearch() {
  116. searchActive = true
  117. }
  118. func endSearch() {
  119. searchActive = false
  120. searchText = ""
  121. resetSearch()
  122. }
  123. var emptySearchText: String? {
  124. if searchActive && numberOfSections == 0 {
  125. return searchText
  126. }
  127. return nil
  128. }
  129. // MARK: - search
  130. func updateSearchResultSections() {
  131. var sections: [ChatListSectionType] = []
  132. if let chatList = searchResultChatList, chatList.length > 0 {
  133. sections.append(searchResultsChatsSection)
  134. }
  135. if !searchResultContactIds.isEmpty {
  136. sections.append(searchResultsContactsSection)
  137. }
  138. searchResultSections = sections
  139. }
  140. func resetSearch() {
  141. searchResultChatList = nil
  142. searchResultContactIds = []
  143. updateSearchResultSections()
  144. }
  145. func filterContentForSearchText(_ searchText: String) {
  146. if !searchText.isEmpty {
  147. filterAndUpdateList(searchText: searchText)
  148. } else {
  149. // when search input field empty we show default chatList
  150. resetSearch()
  151. }
  152. onChatListUpdate?()
  153. }
  154. func filterAndUpdateList(searchText: String) {
  155. // #1 - search for chats with searchPattern in title
  156. let flags = DC_GCL_ADD_ALLDONE_HINT | DC_GCL_FOR_FORWARDING | DC_GCL_NO_SPECIALS
  157. searchResultChatList = dcContext.getChatlist(flags: flags, queryString: searchText, queryId: 0)
  158. // #2 - search for 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. }