Преглед на файлове

Merge pull request #1140 from deltachat/refresh-chatlist-in-bg

speed up chatlist and globel search
bjoern преди 4 години
родител
ревизия
fcc973b3a1
променени са 3 файла, в които са добавени 127 реда и са изтрити 40 реда
  1. 6 0
      DcCore/DcCore/DC/Wrapper.swift
  2. 35 14
      deltachat-ios/Controller/ChatListController.swift
  3. 86 26
      deltachat-ios/ViewModel/ChatListViewModel.swift

+ 6 - 0
DcCore/DcCore/DC/Wrapper.swift

@@ -53,7 +53,10 @@ public class DcContext {
     }
 
     public func getContacts(flags: Int32, queryString: String? = nil) -> [Int] {
+        let start = CFAbsoluteTimeGetCurrent()
         let cContacts = dc_get_contacts(contextPointer, UInt32(flags), queryString)
+        let diff = CFAbsoluteTimeGetCurrent() - start
+        logger?.info("⏰ getContacts: \(diff) s")
         return DcUtils.copyAndFreeArray(inputArray: cContacts)
     }
 
@@ -426,10 +429,13 @@ public class DcContext {
     }
 
     public func searchMessages(chatId: Int = 0, searchText: String) -> [Int] {
+        let start = CFAbsoluteTimeGetCurrent()
         guard let arrayPointer = dc_search_msgs(contextPointer, UInt32(chatId), searchText) else {
             return []
         }
         let messageIds = DcUtils.copyAndFreeArray(inputArray: arrayPointer)
+        let diff = CFAbsoluteTimeGetCurrent() - start
+        logger?.info("⏰ searchMessages: \(diff) s")
         return messageIds
     }
 

+ 35 - 14
deltachat-ios/Controller/ChatListController.swift

@@ -108,22 +108,20 @@ class ChatListController: UITableViewController {
             forName: dcNotificationChanged,
             object: nil,
             queue: nil) { [weak self] _ in
-                self?.viewModel.refreshData()
-
-        }
+                self?.refreshInBg()
+            }
         msgsNoticedObserver = nc.addObserver(
             forName: dcMsgsNoticed,
             object: nil,
             queue: nil) { [weak self] _ in
-                self?.viewModel.refreshData()
-
-        }
+                self?.refreshInBg()
+            }
         incomingMsgObserver = nc.addObserver(
             forName: dcNotificationIncoming,
             object: nil,
             queue: nil) { [weak self] _ in
-                self?.viewModel.refreshData()
-        }
+                self?.refreshInBg()
+            }
         nc.addObserver(
             self,
             selector: #selector(applicationDidBecomeActive(_:)),
@@ -173,7 +171,32 @@ class ChatListController: UITableViewController {
     @objc func applicationDidBecomeActive(_ notification: NSNotification) {
         if navigationController?.visibleViewController == self {
             startTimer()
-            viewModel.refreshData()
+            refreshInBg()
+        }
+    }
+
+    private var inBgRefresh = false
+    private var needsAnotherBgRefresh = false
+    private func refreshInBg() {
+        if inBgRefresh {
+            needsAnotherBgRefresh = true
+        } else {
+            inBgRefresh = true
+            DispatchQueue.global(qos: .userInteractive).async { [weak self] in
+                // do at least one refresh, without inital delay
+                // (refreshData() calls handleChatListUpdate() on main thread when done)
+                self?.needsAnotherBgRefresh = false
+                self?.viewModel.refreshData()
+
+                // do subsequent refreshes with a delay of 500ms
+                while self?.needsAnotherBgRefresh != false {
+                    usleep(500000)
+                    self?.needsAnotherBgRefresh = false
+                    self?.viewModel.refreshData()
+                }
+
+                self?.inBgRefresh = false
+            }
         }
     }
 
@@ -191,8 +214,8 @@ class ChatListController: UITableViewController {
     @objc func cancelButtonPressed() {
         // cancel forwarding
         RelayHelper.sharedInstance.cancel()
-        viewModel.refreshData()
         updateTitle()
+        refreshInBg()
     }
 
     override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
@@ -353,9 +376,7 @@ class ChatListController: UITableViewController {
         // check if the timer is not yet started
         if !(timer?.isValid ?? false) {
             timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { [weak self] _ in
-                DispatchQueue.main.async { [weak self] in
-                    self?.viewModel.refreshData()
-                }
+                self?.refreshInBg()
             }
         }
     }
@@ -426,7 +447,7 @@ class ChatListController: UITableViewController {
     private func deleteChat(chatId: Int, animated: Bool) {
         if !animated {
             _ = viewModel.deleteChat(chatId: chatId)
-            viewModel.refreshData()
+            refreshInBg()
             return
         }
 

+ 86 - 26
deltachat-ios/ViewModel/ChatListViewModel.swift

@@ -34,6 +34,9 @@ class ChatListViewModel: NSObject, ChatListViewModelProtocol {
 
     var onChatListUpdate: VoidFunction?
 
+    private var inBgSearch = false
+    private var needsAnotherBgSearch = false
+
     enum ChatListSectionType {
         case chats
         case contacts
@@ -79,8 +82,14 @@ class ChatListViewModel: NSObject, ChatListViewModelProtocol {
             gclFlags |= DC_GCL_FOR_FORWARDING
         }
         self.chatList = dcContext.getChatlist(flags: gclFlags, queryString: nil, queryId: 0)
-        if notifyListener {
-            onChatListUpdate?()
+        if notifyListener, let onChatListUpdate = onChatListUpdate {
+            if Thread.isMainThread {
+                onChatListUpdate()
+            } else {
+                DispatchQueue.main.async {
+                    onChatListUpdate()
+                }
+            }
         }
     }
 
@@ -258,7 +267,7 @@ private extension ChatListViewModel {
     }
 
     // MARK: - search
-    func updateSearchResultSections() {
+    private func updateSearchResultSections() {
         var sections: [ChatListSectionType] = []
         if let chatList = searchResultChatList, chatList.length > 0 {
             sections.append(searchResultsChatsSection)
@@ -272,46 +281,97 @@ private extension ChatListViewModel {
         searchResultSections = sections
     }
 
-    func resetSearch() {
+    private func resetSearch() {
         searchResultChatList = nil
         searchResultContactIds = []
         searchResultMessageIds = []
         updateSearchResultSections()
     }
 
-    func filterContentForSearchText(_ searchText: String) {
-           if !searchText.isEmpty {
-               filterAndUpdateList(searchText: searchText)
-           } else {
-               // when search input field empty we show default chatList
-               resetSearch()
-           }
-           onChatListUpdate?()
-       }
+    private func filterContentForSearchText(_ searchText: String) {
+        if !searchText.isEmpty {
+            filterAndUpdateList(searchText: searchText)
+        } else {
+            // when search input field empty we show default chatList
+            resetSearch()
+        }
+        if let onChatListUpdate = onChatListUpdate {
+            if Thread.isMainThread {
+                onChatListUpdate()
+            } else {
+                DispatchQueue.main.async {
+                    onChatListUpdate()
+                }
+            }
+        }
+    }
 
     func filterAndUpdateList(searchText: String) {
+        var overallCnt = 0
+
+        // #1 chats with searchPattern in title bar
+        searchResultChatList = dcContext.getChatlist(flags: DC_GCL_NO_SPECIALS, queryString: searchText, queryId: 0)
+        if let chatlist = searchResultChatList {
+            overallCnt += chatlist.length
+        }
+
+        // #2 contacts with searchPattern in name or in email
+        if searchText != self.searchText && overallCnt > 0 {
+            logger.info("... skipping getContacts and searchMessages, more recent search pending")
+            searchResultContactIds = []
+            searchResultMessageIds = []
+            updateSearchResultSections()
+            return
+        }
 
-           // #1 chats with searchPattern in title bar
-           var flags: Int32 = 0
-           flags |= DC_GCL_NO_SPECIALS
-           searchResultChatList = dcContext.getChatlist(flags: flags, queryString: searchText, queryId: 0)
+        searchResultContactIds = dcContext.getContacts(flags: DC_GCL_ADD_SELF, queryString: searchText)
+        overallCnt += searchResultContactIds.count
 
-           // #2 contacts with searchPattern in name or in email
-           searchResultContactIds = dcContext.getContacts(flags: DC_GCL_ADD_SELF, queryString: searchText)
+        // #3 messages with searchPattern (filtered by dc_core)
+        if searchText != self.searchText && overallCnt > 0 {
+            logger.info("... skipping searchMessages, more recent search pending")
+            searchResultMessageIds = []
+            updateSearchResultSections()
+            return
+        }
+
+        if searchText.count <= 1 {
+            logger.info("... skipping searchMessages, string too short")
+            searchResultMessageIds = []
+            updateSearchResultSections()
+            return
+        }
 
-           // #3 messages with searchPattern (filtered by dc_core)
-           searchResultMessageIds = dcContext.searchMessages(searchText: searchText)
-           updateSearchResultSections()
-       }
+        searchResultMessageIds = dcContext.searchMessages(searchText: searchText)
+        updateSearchResultSections()
+    }
 }
 
 // MARK: UISearchResultUpdating
 extension ChatListViewModel: UISearchResultsUpdating {
     func updateSearchResults(for searchController: UISearchController) {
+
         self.searchText = searchController.searchBar.text ?? ""
-        if let searchText = searchController.searchBar.text {
-            filterContentForSearchText(searchText)
-            return
+
+        if inBgSearch {
+            needsAnotherBgSearch = true
+            logger.info("... search call debounced")
+        } else {
+            inBgSearch = true
+            DispatchQueue.global(qos: .userInteractive).async { [weak self] in
+                usleep(100000)
+                self?.needsAnotherBgSearch = false
+                self?.filterContentForSearchText(self?.searchText ?? "")
+
+                while self?.needsAnotherBgSearch != false {
+                    usleep(100000)
+                    self?.needsAnotherBgSearch = false
+                    logger.info("... executing debounced search call")
+                    self?.filterContentForSearchText(self?.searchText ?? "")
+                }
+
+                self?.inBgSearch = false
+            }
         }
     }
 }