|  |  |  | // | 
					
						
							|  |  |  | //  Copyright (c) 2017 Open Whisper Systems. All rights reserved. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import Foundation | 
					
						
							|  |  |  | import SignalServiceKit | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @objc | 
					
						
							|  |  |  | public class ConversationSearcher: NSObject { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @objc | 
					
						
							|  |  |  |     public static let shared: ConversationSearcher = ConversationSearcher() | 
					
						
							|  |  |  |     override private init() { | 
					
						
							|  |  |  |         super.init() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @objc(filterThreads:withSearchText:) | 
					
						
							|  |  |  |     public func filterThreads(_ threads: [TSThread], searchText: String) -> [TSThread] { | 
					
						
							|  |  |  |         guard searchText.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 else { | 
					
						
							|  |  |  |             return threads | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return threads.filter { thread in | 
					
						
							|  |  |  |             switch thread { | 
					
						
							|  |  |  |             case let groupThread as TSGroupThread: | 
					
						
							|  |  |  |                 return self.groupThreadSearcher.matches(item: groupThread, query: searchText) | 
					
						
							|  |  |  |             case let contactThread as TSContactThread: | 
					
						
							|  |  |  |                 return self.contactThreadSearcher.matches(item: contactThread, query: searchText) | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 owsFail("Unexpected thread type: \(thread)") | 
					
						
							|  |  |  |                 return false | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @objc(filterGroupThreads:withSearchText:) | 
					
						
							|  |  |  |     public func filterGroupThreads(_ groupThreads: [TSGroupThread], searchText: String) -> [TSGroupThread] { | 
					
						
							|  |  |  |         guard searchText.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 else { | 
					
						
							|  |  |  |             return groupThreads | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return groupThreads.filter { groupThread in | 
					
						
							|  |  |  |             return self.groupThreadSearcher.matches(item: groupThread, query: searchText) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @objc(filterSignalAccounts:withSearchText:) | 
					
						
							|  |  |  |     public func filterSignalAccounts(_ signalAccounts: [SignalAccount], searchText: String) -> [SignalAccount] { | 
					
						
							|  |  |  |         guard searchText.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 else { | 
					
						
							|  |  |  |             return signalAccounts | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return signalAccounts.filter { signalAccount in | 
					
						
							|  |  |  |             self.signalAccountSearcher.matches(item: signalAccount, query: searchText) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // MARK: - Helpers | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // MARK: Searchers | 
					
						
							|  |  |  |     private lazy var groupThreadSearcher: Searcher<TSGroupThread> = Searcher { (groupThread: TSGroupThread) in | 
					
						
							|  |  |  |         let groupName = groupThread.groupModel.groupName | 
					
						
							|  |  |  |         let memberStrings = groupThread.groupModel.groupMemberIds.map { recipientId in | 
					
						
							|  |  |  |             self.indexingString(recipientId: recipientId) | 
					
						
							|  |  |  |             }.joined(separator: " ") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return "\(memberStrings) \(groupName ?? "")" | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private lazy var contactThreadSearcher: Searcher<TSContactThread> = Searcher { (contactThread: TSContactThread) in | 
					
						
							|  |  |  |         let recipientId = contactThread.contactIdentifier() | 
					
						
							|  |  |  |         return self.indexingString(recipientId: recipientId) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private lazy var signalAccountSearcher: Searcher<SignalAccount> = Searcher { (signalAccount: SignalAccount) in | 
					
						
							|  |  |  |         let recipientId = signalAccount.recipientId | 
					
						
							|  |  |  |         return self.indexingString(recipientId: recipientId) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private var contactsManager: OWSContactsManager { | 
					
						
							|  |  |  |         return Environment.current().contactsManager | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private func indexingString(recipientId: String) -> String { | 
					
						
							|  |  |  |         let contactName = contactsManager.displayName(forPhoneIdentifier: recipientId) | 
					
						
							|  |  |  |         let profileName = contactsManager.profileName(forRecipientId: recipientId) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return "\(recipientId) \(contactName) \(profileName ?? "")" | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |