WIP: FTS - wired up Search VC

-[] Backend
  -[] indexes e5.25
    -[x] wire up results: Contacts / Conversations / Messages actual: 3hr
    -[ ] group thread est: actual:
      -[x] group name actual: e.25
      -[ ] group member name: e.25
      -[ ] group member number: e.25
    -[ ] contact thread e.5
      -[ ] name
      -[ ] number
    -[ ] messages e1
      -[ ] content
-[] Frontend e10.75
  -[x] wire up VC's a.5
  -[x] show search results only when search box has content a.25
  -[] show search results: Contact / Conversation / Messages e2
  -[] tapping thread search result takes you to conversation e1
  -[] tapping message search result takes you to message e1
  -[] show snippet text for matched message e1
  -[] highlight matched text in thread e3
  -[] go to next search result in thread e2
pull/1/head
Michael Kirk 7 years ago
parent 429af7854a
commit ffea3a020f

@ -412,6 +412,7 @@
45FBC5D11DF8592E00E9B410 /* SignalCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45FBC5D01DF8592E00E9B410 /* SignalCall.swift */; };
4AC4EA13C8A444455DAB351F /* Pods_SignalMessaging.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 264242150E87D10A357DB07B /* Pods_SignalMessaging.framework */; };
4C20B2B720CA0034001BAC90 /* ThreadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4542DF51208B82E9007B4E76 /* ThreadViewModel.swift */; };
4C20B2B920CA10DE001BAC90 /* ConversationSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C20B2B820CA10DE001BAC90 /* ConversationSearchViewController.swift */; };
70377AAB1918450100CAF501 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70377AAA1918450100CAF501 /* MobileCoreServices.framework */; };
768A1A2B17FC9CD300E00ED8 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 768A1A2A17FC9CD300E00ED8 /* libz.dylib */; };
76C87F19181EFCE600C4ACAB /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76C87F18181EFCE600C4ACAB /* MediaPlayer.framework */; };
@ -1065,6 +1066,7 @@
45FBC59A1DF8575700E9B410 /* CallKitCallManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallKitCallManager.swift; sourceTree = "<group>"; };
45FBC5D01DF8592E00E9B410 /* SignalCall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalCall.swift; sourceTree = "<group>"; };
45FDA43420A4D22700396358 /* OWSNavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSNavigationBar.swift; sourceTree = "<group>"; };
4C20B2B820CA10DE001BAC90 /* ConversationSearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationSearchViewController.swift; sourceTree = "<group>"; };
69349DE607F5BA6036C9AC60 /* Pods-SignalShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalShareExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
70377AAA1918450100CAF501 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
748A5CAEDD7C919FC64C6807 /* Pods_SignalTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -1408,6 +1410,7 @@
34386A50207D0C01009F5D9C /* HomeViewCell.m */,
34386A4F207D0C01009F5D9C /* HomeViewController.h */,
34386A4D207D0C01009F5D9C /* HomeViewController.m */,
4C20B2B820CA10DE001BAC90 /* ConversationSearchViewController.swift */,
);
path = HomeView;
sourceTree = "<group>";
@ -3214,6 +3217,7 @@
45794E861E00620000066731 /* CallUIAdapter.swift in Sources */,
340FC8BA204DAC8D007AEB0F /* FingerprintViewScanController.m in Sources */,
4585C4681ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift in Sources */,
4C20B2B920CA10DE001BAC90 /* ConversationSearchViewController.swift in Sources */,
450D19131F85236600970622 /* RemoteVideoView.m in Sources */,
B6B9ECFC198B31BA00C620D3 /* PushManager.m in Sources */,
34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */,

@ -0,0 +1,148 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
@objc
class ConversationSearchViewController: UITableViewController {
var searchResults: ConversationSearchResults = ConversationSearchResults.empty()
enum SearchSection: Int {
case conversations = 0
case contacts = 1
case messages = 2
}
// MARK: View Lifecyle
override func viewDidLoad() {
super.viewDidLoad()
self.view.isHidden = true
self.view.backgroundColor = UIColor.yellow
}
// MARK: UITableViewDelegate
override public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let searchSection = SearchSection(rawValue: section) else {
owsFail("unknown section: \(section)")
return 0
}
switch searchSection {
case .conversations:
return searchResults.conversations.count
case .contacts:
return searchResults.contacts.count
case .messages:
return searchResults.messages.count
}
}
/*
// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
@available(iOS 2.0, *)
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
@available(iOS 2.0, *)
optional public func numberOfSections(in tableView: UITableView) -> Int // Default is 1 if not implemented
@available(iOS 2.0, *)
optional public func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? // fixed font style. use custom view (UILabel) if you want something different
@available(iOS 2.0, *)
optional public func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String?
// Editing
// Individual rows can opt out of having the -editing property set for them. If not implemented, all rows are assumed to be editable.
@available(iOS 2.0, *)
optional public func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool
// Moving/reordering
// Allows the reorder accessory view to optionally be shown for a particular row. By default, the reorder control will be shown only if the datasource implements -tableView:moveRowAtIndexPath:toIndexPath:
@available(iOS 2.0, *)
optional public func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool
// Index
@available(iOS 2.0, *)
optional public func sectionIndexTitles(for tableView: UITableView) -> [String]? // return list of section titles to display in section index view (e.g. "ABCD...Z#")
@available(iOS 2.0, *)
optional public func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int // tell table which section corresponds to section title/index (e.g. "B",1))
// Data manipulation - insert and delete support
// After a row has the minus or plus button invoked (based on the UITableViewCellEditingStyle for the cell), the dataSource must commit the change
// Not called for edit actions using UITableViewRowAction - the action's handler will be invoked instead
@available(iOS 2.0, *)
optional public func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
// Data manipulation - reorder / moving support
@available(iOS 2.0, *)
optional public func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)
*/
}
extension ConversationSearchViewController: UISearchBarDelegate {
// @available(iOS 2.0, *)
// optional public func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool // return NO to not become first responder
//
// @available(iOS 2.0, *)
// optional public func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) // called when text starts editing
//
// @available(iOS 2.0, *)
// optional public func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool // return NO to not resign first responder
//
// @available(iOS 2.0, *)
// optional public func searchBarTextDidEndEditing(_ searchBar: UISearchBar) // called when text ends editing
//
public func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
guard searchText.stripped.count > 0 else {
self.view.isHidden = true
return
}
self.view.isHidden = false
}
//
// @available(iOS 3.0, *)
// optional public func searchBar(_ searchBar: UISearchBar, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool // called before text changes
//
//
// @available(iOS 2.0, *)
// optional public func searchBarSearchButtonClicked(_ searchBar: UISearchBar) // called when keyboard search button pressed
//
// @available(iOS 2.0, *)
// optional public func searchBarBookmarkButtonClicked(_ searchBar: UISearchBar) // called when bookmark button pressed
//
// @available(iOS 2.0, *)
// optional public func searchBarCancelButtonClicked(_ searchBar: UISearchBar) // called when cancel button pressed
//
// @available(iOS 3.2, *)
// optional public func searchBarResultsListButtonClicked(_ searchBar: UISearchBar) // called when search results button pressed
//
//
// @available(iOS 3.0, *)
// optional public func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int)
}

@ -54,6 +54,11 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
@property (nonatomic) BOOL shouldObserveDBModifications;
@property (nonatomic) BOOL hasBeenPresented;
// Mark: Search
@property (nonatomic) UISearchBar *searchBar;
@property (nonatomic) ConversationSearchViewController *searchController;
// Dependencies
@property (nonatomic, readonly) AccountManager *accountManager;
@ -238,7 +243,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
action:@selector(pullToRefreshPerformed:)
forControlEvents:UIControlEventValueChanged];
[self.tableView insertSubview:pullToRefreshView atIndex:0];
[self updateReminderViews];
}
@ -261,7 +266,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
[super viewDidLoad];
self.editingDbConnection = OWSPrimaryStorage.sharedManager.newDatabaseConnection;
// Create the database connection.
[self uiDatabaseConnection];
@ -290,6 +295,28 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
[self registerForPreviewingWithDelegate:self sourceView:self.tableView];
}
// Search
UISearchBar *searchBar = [UISearchBar new];
_searchBar = searchBar;
searchBar.searchBarStyle = UISearchBarStyleMinimal;
searchBar.placeholder = NSLocalizedString(@"HOME_VIEW_CONVERSATION_SEARCHBAR_PLACEHOLDER", @"Placeholder text for search bar which filters conversations.");
searchBar.backgroundColor = [UIColor whiteColor];
[searchBar sizeToFit];
// Setting tableHeader calls numberOfSections, which must happen after updateMappings has been called at least once.
ConversationSearchViewController *searchController = [ConversationSearchViewController new];
self.searchController = searchController;
[self addChildViewController:searchController];
searchController.view.frame = self.view.frame;
[self.view addSubview:searchController.view];
// TODO - better/more flexible way to pin below search bar?
[searchController.view autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsMake(58, 0, 0, 0)];
searchBar.delegate = searchController;
OWSAssert(self.tableView.tableHeaderView == nil);
self.tableView.tableHeaderView = self.searchBar;
[self updateBarButtonItems];
}

@ -986,6 +986,9 @@
/* A label for conversations with blocked users. */
"HOME_VIEW_BLOCKED_CONTACT_CONVERSATION" = "Blocked";
/* Placeholder text for search bar which filters conversations. */
"HOME_VIEW_CONVERSATION_SEARCHBAR_PLACEHOLDER" = "Search";
/* Title for the home view's 'archive' mode. */
"HOME_VIEW_TITLE_ARCHIVE" = "Archive";

@ -6,8 +6,12 @@ import Foundation
public extension String {
var stripped: String {
return self.trimmingCharacters(in: .whitespacesAndNewlines)
}
// Truncates string to be less than or equal to byteCount, while ensuring we never truncate partial characters for multibyte characters.
public func truncated(toByteCount byteCount: UInt) -> String? {
func truncated(toByteCount byteCount: UInt) -> String? {
var lowerBoundCharCount = 0
var upperBoundCharCount = self.count

@ -15,17 +15,20 @@ public class ConversationSearchItem: NSObject {
}
}
@objc
public class ConversationSearchResults: NSObject {
let conversations: [ConversationSearchItem]
let contacts: [ConversationSearchItem]
let messages: [ConversationSearchItem]
public class ConversationSearchResults {
public let conversations: [ConversationSearchItem]
public let contacts: [ConversationSearchItem]
public let messages: [ConversationSearchItem]
public init(conversations: [ConversationSearchItem], contacts: [ConversationSearchItem], messages: [ConversationSearchItem]) {
self.conversations = conversations
self.contacts = contacts
self.messages = messages
}
public class func empty() -> ConversationSearchResults {
return ConversationSearchResults(conversations: [], contacts: [], messages: [])
}
}
@objc
@ -40,7 +43,6 @@ public class ConversationSearcher: NSObject {
super.init()
}
@objc
public func results(searchText: String, transaction: YapDatabaseReadTransaction) -> ConversationSearchResults {
// TODO limit results, prioritize conversations, then contacts, then messages.

Loading…
Cancel
Save