From 4abd3f58cdf3b3bc7484cab84e04cb77ce30f41e Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 12 Jun 2018 15:04:05 -0400 Subject: [PATCH] Order the search results. --- .../ConversationSearchViewController.swift | 2 +- SignalMessaging/contacts/OWSContactsManager.h | 1 + .../utils/ConversationSearcher.swift | 70 ++++++++++++++++--- 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift b/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift index a68001c76..0dc8a1e75 100644 --- a/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift +++ b/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift @@ -214,7 +214,7 @@ class ConversationSearchViewController: UITableViewController { // TODO: debounce? self.uiDatabaseConnection.read { transaction in - self.searchResultSet = self.searcher.results(searchText: searchText, transaction: transaction) + self.searchResultSet = self.searcher.results(searchText: searchText, transaction: transaction, contactsManager: self.contactsManager) } // TODO: more perfomant way to do this? diff --git a/SignalMessaging/contacts/OWSContactsManager.h b/SignalMessaging/contacts/OWSContactsManager.h index 90ee69dc5..7dc66d88c 100644 --- a/SignalMessaging/contacts/OWSContactsManager.h +++ b/SignalMessaging/contacts/OWSContactsManager.h @@ -36,6 +36,7 @@ extern NSString *const OWSContactsManagerSignalAccountsDidChangeNotification; - (BOOL)hasSignalAccountForRecipientId:(NSString *)recipientId; - (void)loadSignalAccountsFromCache; + #pragma mark - System Contact Fetching // Must call `requestSystemContactsOnce` before accessing this method diff --git a/SignalMessaging/utils/ConversationSearcher.swift b/SignalMessaging/utils/ConversationSearcher.swift index 2c68cc250..297744243 100644 --- a/SignalMessaging/utils/ConversationSearcher.swift +++ b/SignalMessaging/utils/ConversationSearcher.swift @@ -5,28 +5,61 @@ import Foundation import SignalServiceKit -public class ConversationSearchResult { +public class ConversationSearchResult: NSObject, Comparable { public let thread: ThreadViewModel public let messageId: String? public let snippet: String? - init(thread: ThreadViewModel, messageId: String?, snippet: String?) { + private let sortKey: UInt64 + + init(thread: ThreadViewModel, messageId: String?, snippet: String?, sortKey: UInt64) { self.thread = thread self.messageId = messageId self.snippet = snippet + self.sortKey = sortKey + } + + // Mark: Comparable + + public static func < (lhs: ConversationSearchResult, rhs: ConversationSearchResult) -> Bool { + return lhs.sortKey < rhs.sortKey + } + + // MARK: Equatable + + public static func == (lhs: ConversationSearchResult, rhs: ConversationSearchResult) -> Bool { + if lhs.thread.threadRecord.uniqueId != rhs.thread.threadRecord.uniqueId { + return false + } + return NSObject.isNullableObject(lhs.messageId as NSObject?, equalTo: rhs.messageId as NSObject?) } } -public class ContactSearchResult { +public class ContactSearchResult: NSObject, Comparable { public let signalAccount: SignalAccount public var recipientId: String { return signalAccount.recipientId } - init(signalAccount: SignalAccount) { + private var sortKey: String = "" + + init(signalAccount: SignalAccount, sortKey: String) { self.signalAccount = signalAccount + self.sortKey = sortKey + } + + // Mark: Comparable + + public static func < (lhs: ContactSearchResult, rhs: ContactSearchResult) -> Bool { + return lhs.sortKey < rhs.sortKey + } + + // MARK: Equatable + + public static func == (lhs: ContactSearchResult, rhs: ContactSearchResult) -> Bool { + return lhs.recipientId == rhs.recipientId } } @@ -64,7 +97,10 @@ public class ConversationSearcher: NSObject { super.init() } - public func results(searchText: String, transaction: YapDatabaseReadTransaction) -> SearchResultSet { + public func results(searchText: String, + transaction: YapDatabaseReadTransaction, + contactsManager: OWSContactsManager) -> SearchResultSet { + var conversations: [ConversationSearchResult] = [] var contacts: [ContactSearchResult] = [] var messages: [ConversationSearchResult] = [] @@ -75,22 +111,29 @@ public class ConversationSearcher: NSObject { if let thread = match as? TSThread { let threadViewModel = ThreadViewModel(thread: thread, transaction: transaction) let snippet: String? = thread.lastMessageText(transaction: transaction) - let searchResult = ConversationSearchResult(thread: threadViewModel, messageId: nil, snippet: snippet) + let sortKey = NSDate.ows_millisecondsSince1970(for: threadViewModel.lastMessageDate) + let searchResult = ConversationSearchResult(thread: threadViewModel, messageId: nil, snippet: snippet, sortKey: sortKey) if let contactThread = thread as? TSContactThread { let recipientId = contactThread.contactIdentifier() existingConversationRecipientIds.insert(recipientId) } + conversations.append(searchResult) } else if let message = match as? TSMessage { let thread = message.thread(with: transaction) let threadViewModel = ThreadViewModel(thread: thread, transaction: transaction) - let searchResult = ConversationSearchResult(thread: threadViewModel, messageId: message.uniqueId, snippet: snippet) - + let sortKey = message.timestamp + let searchResult = ConversationSearchResult(thread: threadViewModel, messageId: message.uniqueId, snippet: snippet, sortKey: sortKey) messages.append(searchResult) } else if let signalAccount = match as? SignalAccount { - let searchResult = ContactSearchResult(signalAccount: signalAccount) + let anyFont = UIFont.systemFont(ofSize: 12) + let sortKey = contactsManager.attributedStringForConversationTitle(withPhoneIdentifier: signalAccount.recipientId, + primaryFont: anyFont, + secondaryFont: anyFont).string + + let searchResult = ContactSearchResult(signalAccount: signalAccount, sortKey: sortKey) contacts.append(searchResult) } else { owsFail("\(self.logTag) in \(#function) unhandled item: \(match)") @@ -98,7 +141,14 @@ public class ConversationSearcher: NSObject { } // Only show contacts which were not included in an existing 1:1 conversation. - let otherContacts: [ContactSearchResult] = contacts.filter { !existingConversationRecipientIds.contains($0.recipientId) } + var otherContacts: [ContactSearchResult] = contacts.filter { !existingConversationRecipientIds.contains($0.recipientId) } + + // Order the conversation and message results in reverse chronological order. + // The contact results are pre-sorted by display name. + conversations = conversations.sorted().reversed() + messages = messages.sorted().reversed() + // Order "other" contact results by display name. + otherContacts = otherContacts.sorted() return SearchResultSet(searchText: searchText, conversations: conversations, contacts: otherContacts, messages: messages) }