session-ios/SignalMessaging/contacts/SelectThreadViewController.m
Michael Kirk 3ecf0a7537 Cancelling dismisses share extension, remove "import with signal"
Usinig the new ShareViewDelegate to dismiss the share extension, might
have broken the "import with signal" functionality. But because we
want to remove it anyway, I've done that now, rather than fix it up.

// FREEBIE
2017-12-07 17:12:03 -05:00

307 lines
11 KiB
Objective-C

//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "SelectThreadViewController.h"
#import "BlockListUIUtils.h"
#import "ContactTableViewCell.h"
#import "ContactsViewHelper.h"
#import "Environment.h"
#import "NSString+OWS.h"
#import "OWSContactsManager.h"
#import "OWSTableViewController.h"
#import "ThreadViewHelper.h"
#import "UIColor+OWS.h"
#import "UIFont+OWS.h"
#import "UIView+OWS.h"
#import <SignalMessaging/SignalMessaging-Swift.h>
#import <SignalServiceKit/PhoneNumber.h>
#import <SignalServiceKit/SignalAccount.h>
#import <SignalServiceKit/TSAccountManager.h>
#import <SignalServiceKit/TSContactThread.h>
#import <SignalServiceKit/TSThread.h>
NS_ASSUME_NONNULL_BEGIN
@interface SelectThreadViewController () <OWSTableViewControllerDelegate,
ThreadViewHelperDelegate,
ContactsViewHelperDelegate,
UISearchBarDelegate>
@property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper;
@property (nonatomic, readonly) ConversationSearcher *conversationSearcher;
@property (nonatomic, readonly) ThreadViewHelper *threadViewHelper;
@property (nonatomic, readonly) OWSTableViewController *tableViewController;
@property (nonatomic, readonly) UISearchBar *searchBar;
@end
#pragma mark -
@implementation SelectThreadViewController
- (void)loadView
{
[super loadView];
self.navigationItem.leftBarButtonItem =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop
target:self
action:@selector(dismissPressed:)];
self.view.backgroundColor = [UIColor whiteColor];
_contactsViewHelper = [[ContactsViewHelper alloc] initWithDelegate:self];
_conversationSearcher = ConversationSearcher.shared;
_threadViewHelper = [ThreadViewHelper new];
_threadViewHelper.delegate = self;
[self createViews];
[self updateTableContents];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController.navigationBar setTranslucent:NO];
}
- (void)createViews
{
OWSAssert(self.selectThreadViewDelegate);
// Search
UISearchBar *searchBar = [UISearchBar new];
_searchBar = searchBar;
searchBar.searchBarStyle = UISearchBarStyleMinimal;
searchBar.delegate = self;
searchBar.placeholder = NSLocalizedString(@"SEARCH_BYNAMEORNUMBER_PLACEHOLDER_TEXT", @"");
searchBar.backgroundColor = [UIColor whiteColor];
[searchBar sizeToFit];
UIView *header = [self.selectThreadViewDelegate createHeaderWithSearchBar:searchBar];
// Table
_tableViewController = [OWSTableViewController new];
_tableViewController.delegate = self;
[self.view addSubview:self.tableViewController.view];
[_tableViewController.view autoPinWidthToSuperview];
[_tableViewController.view autoPinToTopLayoutGuideOfViewController:self withInset:0];
if (header) {
_tableViewController.tableView.tableHeaderView = header;
} else {
_tableViewController.tableView.tableHeaderView = searchBar;
}
[self autoPinViewToBottomGuideOrKeyboard:self.tableViewController.view];
}
#pragma mark - UISearchBarDelegate
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[self updateTableContents];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[self updateTableContents];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
[self updateTableContents];
}
- (void)searchBarResultsListButtonClicked:(UISearchBar *)searchBar
{
[self updateTableContents];
}
- (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope
{
[self updateTableContents];
}
#pragma mark - Table Contents
- (void)updateTableContents
{
__weak SelectThreadViewController *weakSelf = self;
ContactsViewHelper *helper = self.contactsViewHelper;
OWSTableContents *contents = [OWSTableContents new];
// Existing threads are listed first, ordered by most recently active
OWSTableSection *recentChatsSection = [OWSTableSection new];
recentChatsSection.headerTitle = NSLocalizedString(
@"SELECT_THREAD_TABLE_RECENT_CHATS_TITLE", @"Table section header for recently active conversations");
for (TSThread *thread in [self filteredThreadsWithSearchText]) {
[recentChatsSection addItem:[OWSTableItem itemWithCustomCellBlock:^{
SelectThreadViewController *strongSelf = weakSelf;
OWSCAssert(strongSelf);
// To be consistent with the threads (above), we use ContactTableViewCell
// instead of InboxTableViewCell to present contacts and threads.
ContactTableViewCell *cell = [ContactTableViewCell new];
[cell configureWithThread:thread contactsManager:helper.contactsManager];
return cell;
}
customRowHeight:[ContactTableViewCell rowHeight]
actionBlock:^{
[weakSelf.selectThreadViewDelegate threadWasSelected:thread];
}]];
}
if (recentChatsSection.itemCount > 0) {
[contents addSection:recentChatsSection];
}
// Contacts who don't yet have a thread are listed last
OWSTableSection *otherContactsSection = [OWSTableSection new];
otherContactsSection.headerTitle = NSLocalizedString(
@"SELECT_THREAD_TABLE_OTHER_CHATS_TITLE", @"Table section header for conversations you haven't recently used.");
NSArray<SignalAccount *> *filteredSignalAccounts = [self filteredSignalAccountsWithSearchText];
for (SignalAccount *signalAccount in filteredSignalAccounts) {
[otherContactsSection addItem:[OWSTableItem itemWithCustomCellBlock:^{
SelectThreadViewController *strongSelf = weakSelf;
OWSCAssert(strongSelf);
ContactTableViewCell *cell = [ContactTableViewCell new];
BOOL isBlocked = [helper isRecipientIdBlocked:signalAccount.recipientId];
if (isBlocked) {
cell.accessoryMessage
= NSLocalizedString(@"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked.");
} else {
OWSAssert(cell.accessoryMessage == nil);
}
[cell configureWithSignalAccount:signalAccount contactsManager:helper.contactsManager];
return cell;
}
customRowHeight:[ContactTableViewCell rowHeight]
actionBlock:^{
[weakSelf signalAccountWasSelected:signalAccount];
}]];
}
if (otherContactsSection.itemCount > 0) {
[contents addSection:otherContactsSection];
}
if (recentChatsSection.itemCount + otherContactsSection.itemCount < 1) {
OWSTableSection *emptySection = [OWSTableSection new];
[emptySection
addItem:[OWSTableItem
softCenterLabelItemWithText:NSLocalizedString(@"SETTINGS_BLOCK_LIST_NO_CONTACTS",
@"A label that indicates the user has no Signal contacts.")]];
[contents addSection:emptySection];
}
self.tableViewController.contents = contents;
}
- (void)signalAccountWasSelected:(SignalAccount *)signalAccount
{
OWSAssert(signalAccount);
OWSAssert(self.selectThreadViewDelegate);
ContactsViewHelper *helper = self.contactsViewHelper;
if ([helper isRecipientIdBlocked:signalAccount.recipientId]
&& ![self.selectThreadViewDelegate canSelectBlockedContact]) {
__weak SelectThreadViewController *weakSelf = self;
[BlockListUIUtils showUnblockSignalAccountActionSheet:signalAccount
fromViewController:self
blockingManager:helper.blockingManager
contactsManager:helper.contactsManager
completionBlock:^(BOOL isBlocked) {
if (!isBlocked) {
[weakSelf signalAccountWasSelected:signalAccount];
}
}];
return;
}
__block TSThread *thread = nil;
[[TSStorageManager sharedManager].dbReadWriteConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
thread = [TSContactThread getOrCreateThreadWithContactId:signalAccount.recipientId transaction:transaction];
}];
OWSAssert(thread);
[self.selectThreadViewDelegate threadWasSelected:thread];
}
#pragma mark - Filter
- (NSArray<TSThread *> *)filteredThreadsWithSearchText
{
NSString *searchTerm = [[self.searchBar text] ows_stripped];
return [self.conversationSearcher filterThreads:self.threadViewHelper.threads withSearchText:searchTerm];
}
- (NSArray<SignalAccount *> *)filteredSignalAccountsWithSearchText
{
// We don't want to show a 1:1 thread with Alice and Alice's contact,
// so we de-duplicate by recipientId.
NSArray<TSThread *> *threads = self.threadViewHelper.threads;
NSMutableSet *contactIdsToIgnore = [NSMutableSet new];
for (TSThread *thread in threads) {
if ([thread isKindOfClass:[TSContactThread class]]) {
TSContactThread *contactThread = (TSContactThread *)thread;
[contactIdsToIgnore addObject:contactThread.contactIdentifier];
}
}
NSString *searchString = self.searchBar.text;
NSArray<SignalAccount *> *matchingAccounts =
[self.contactsViewHelper signalAccountsMatchingSearchString:searchString];
return [matchingAccounts
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(SignalAccount *signalAccount,
NSDictionary<NSString *, id> *_Nullable bindings) {
return ![contactIdsToIgnore containsObject:signalAccount.recipientId];
}]];
}
#pragma mark - Events
- (void)dismissPressed:(id)sender
{
[self.searchBar resignFirstResponder];
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - OWSTableViewControllerDelegate
- (void)tableViewWillBeginDragging
{
[self.searchBar resignFirstResponder];
}
#pragma mark - ThreadViewHelperDelegate
- (void)threadListDidChange
{
[self updateTableContents];
}
#pragma mark - ContactsViewHelperDelegate
- (void)contactsViewHelperDidUpdateContacts
{
[self updateTableContents];
}
- (BOOL)shouldHideLocalNumber
{
return NO;
}
@end
NS_ASSUME_NONNULL_END