diff --git a/Session/Home/GlobalSearch/GlobalSearchViewController.swift b/Session/Home/GlobalSearch/GlobalSearchViewController.swift index ae6dc757e..4d1691364 100644 --- a/Session/Home/GlobalSearch/GlobalSearchViewController.swift +++ b/Session/Home/GlobalSearch/GlobalSearchViewController.swift @@ -13,6 +13,7 @@ class GlobalSearchViewController: BaseVC, UITableViewDelegate, UITableViewDataSo } } var recentSearchResults: [String] = Array(Storage.shared.getRecentSearchResults().reversed()) + var defaultSearchResults: HomeScreenSearchResultSet = HomeScreenSearchResultSet.noteToSelfOnly var searchResultSet: HomeScreenSearchResultSet = HomeScreenSearchResultSet.empty private var lastSearchText: String? var searcher: FullTextSearcher { @@ -106,30 +107,10 @@ class GlobalSearchViewController: BaseVC, UITableViewDelegate, UITableViewDataSo var refreshTimer: Timer? private func refreshSearchResults() { - - guard !searchResultSet.isEmpty else { - // To avoid incorrectly showing the "no results" state, - // always search immediately if the current result set is empty. - refreshTimer?.invalidate() - refreshTimer = nil - - updateSearchResults(searchText: searchText) - return - } - - if refreshTimer != nil { - // Don't start a new refresh timer if there's already one active. - return - } - refreshTimer?.invalidate() refreshTimer = WeakTimer.scheduledTimer(timeInterval: 0.1, target: self, userInfo: nil, repeats: false) { [weak self] _ in - guard let self = self else { - return - } - + guard let self = self else { return } self.updateSearchResults(searchText: self.searchText) - self.refreshTimer = nil } } @@ -137,7 +118,7 @@ class GlobalSearchViewController: BaseVC, UITableViewDelegate, UITableViewDataSo let searchText = rawSearchText.stripped guard searchText.count > 0 else { - searchResultSet = HomeScreenSearchResultSet.noteToSelfOnly + searchResultSet = defaultSearchResults lastSearchText = nil reloadTableData() return @@ -159,6 +140,7 @@ class GlobalSearchViewController: BaseVC, UITableViewDelegate, UITableViewDataSo self.searchResultSet = results self.isLoading = false self.reloadTableData() + self.refreshTimer = nil }) } diff --git a/SessionMessagingKit/Database/OWSPrimaryStorage.m b/SessionMessagingKit/Database/OWSPrimaryStorage.m index ae897bf48..b4852c07a 100644 --- a/SessionMessagingKit/Database/OWSPrimaryStorage.m +++ b/SessionMessagingKit/Database/OWSPrimaryStorage.m @@ -67,6 +67,7 @@ void VerifyRegistrationsForPrimaryStorage(OWSStorage *storage) [self loadDatabase]; _dbReadPool = [[YapDatabaseConnectionPool alloc] initWithDatabase:self.database]; + _dbReadPool.connectionLimit = 10; // Increase max read connection limit. Default is 3. _dbReadWriteConnection = [self newDatabaseConnection]; _uiDatabaseConnection = [self newDatabaseConnection]; diff --git a/SessionMessagingKit/Utilities/FullTextSearchFinder.swift b/SessionMessagingKit/Utilities/FullTextSearchFinder.swift index 0320aeaf2..78ea59d5d 100644 --- a/SessionMessagingKit/Utilities/FullTextSearchFinder.swift +++ b/SessionMessagingKit/Utilities/FullTextSearchFinder.swift @@ -30,6 +30,8 @@ public class FullTextSearchFinder: NSObject { private static var tsAccountManager: TSAccountManager { return TSAccountManager.sharedInstance() } + + public var newQueryTimestamp: UInt64 = 0 // MARK: - Querying @@ -89,6 +91,13 @@ public class FullTextSearchFinder: NSObject { guard let ext: YapDatabaseFullTextSearchTransaction = ext(transaction: transaction) else { return } + + // HACK: Full text search is too expensive even though we drop the max search results + // to a reasonable number. And the async read can sometimes block a thread and make + // other read threads wait for it to finish. The timestamp is a workaround to ensure + // only one thread can be running the full text search at one time. + let currentQueryTimestamp = NSDate.millisecondTimestamp() + newQueryTimestamp = currentQueryTimestamp let query = FullTextSearchFinder.query(searchText: searchText) @@ -99,7 +108,7 @@ public class FullTextSearchFinder: NSObject { snippetOptions.endMatchText = "" snippetOptions.numberOfTokens = 5 ext.enumerateKeysAndObjects(matching: query, with: snippetOptions) { (snippet: String, _: String, _: String, object: Any, stop: UnsafeMutablePointer) in - guard searchResultCount < maxSearchResults else { + guard searchResultCount < maxSearchResults && currentQueryTimestamp >= self.newQueryTimestamp else { stop.pointee = true return } diff --git a/SignalUtilitiesKit/Profile Pictures/ProfilePictureView.swift b/SignalUtilitiesKit/Profile Pictures/ProfilePictureView.swift index b91303954..e60c6fe5e 100644 --- a/SignalUtilitiesKit/Profile Pictures/ProfilePictureView.swift +++ b/SignalUtilitiesKit/Profile Pictures/ProfilePictureView.swift @@ -82,7 +82,6 @@ public final class ProfilePictureView : UIView { update() } else { // A one-to-one chat let thread = thread as! TSContactThread - hasTappableProfilePicture = OWSProfileManager.shared().profileAvatar(forRecipientId: thread.contactSessionID()) != nil update(for: thread.contactSessionID()) } } @@ -92,8 +91,10 @@ public final class ProfilePictureView : UIView { func getProfilePicture(of size: CGFloat, for publicKey: String) -> UIImage? { guard !publicKey.isEmpty else { return nil } if let profilePicture = OWSProfileManager.shared().profileAvatar(forRecipientId: publicKey) { + hasTappableProfilePicture = true return profilePicture } else { + hasTappableProfilePicture = false // TODO: Pass in context? let displayName = Storage.shared.getContact(with: publicKey)?.name ?? publicKey return Identicon.generatePlaceholderIcon(seed: publicKey, text: displayName, size: size)