ChatSecure-iOS/ChatSecure/Classes/View Controllers/OTRComposeViewController.m

726 lines
30 KiB
Objective-C

//
// OTRComposeViewController.m
// Off the Record
//
// Created by David Chiles on 3/4/14.
// Copyright (c) 2014 Chris Ballinger. All rights reserved.
//
#import "OTRComposeViewController.h"
#import "OTRBuddy.h"
#import "OTRAccount.h"
#import "OTRDatabaseView.h"
#import "OTRLog.h"
#import "OTRDatabaseManager.h"
#import "OTRDatabaseView.h"
#import "OTRAccountsManager.h"
@import YapDatabase;
@import PureLayout;
@import BButton;
#import "OTRBuddyInfoCell.h"
#import "OTRXMPPManager_Private.h"
#import "OTRNewBuddyViewController.h"
#import "OTRChooseAccountViewController.h"
#import "UITableView+ChatSecure.h"
#import "ChatSecureCoreCompat-Swift.h"
@import OTRAssets;
@interface OTRComposeViewController () <UITableViewDataSource, UITableViewDelegate, OTRYapViewHandlerDelegateProtocol, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, OTRComposeGroupViewControllerDelegate>
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) OTRVerticalStackView *tableViewHeader;
@property (nonatomic, strong) NSLayoutConstraint * tableViewBottomConstraint;
@property (nonatomic, strong) OTRYapViewHandler *viewHandler;
@property (nonatomic, strong) OTRYapViewHandler *searchViewHandler;
@property (nonatomic, strong) UISearchController *searchController;
@property (nonatomic, strong, readonly) YapDatabaseConnection *searchConnection;
@property (nonatomic, strong, readonly) YapDatabaseSearchQueue *searchQueue;
@property (nonatomic, weak, readonly) YapDatabase *database;
@property (nonatomic, weak) YapDatabaseConnection *readWriteConnection;
@property (nonatomic, strong) NSMutableSet <NSString *>*selectedBuddiesIdSet;
@property (nonatomic, strong) UIBarButtonItem *doneBarButtonItem;
@property (nonatomic, strong) UIBarButtonItem *groupBarButtonItem;
@property (nonatomic, strong) UISegmentedControl *inboxArchiveControl;
@end
@implementation OTRComposeViewController
- (instancetype)init {
if (self = [super init]) {
self.selectedBuddiesIdSet = [[NSMutableSet alloc] init];
_database = [OTRDatabaseManager sharedInstance].database;
_readWriteConnection = [OTRDatabaseManager sharedInstance].writeConnection;
_searchConnection = [self.database newConnection];
_searchConnection.name = @"ComposeViewSearchConnection";
_searchQueue = [[YapDatabaseSearchQueue alloc] init];
_selectionModeIsSingle = YES;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
/////////// Navigation Bar ///////////
self.title = COMPOSE_STRING();
UIBarButtonItem * cancelBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelButtonPressed:)];
UIImage *checkImage = [UIImage imageNamed:@"ic-check" inBundle:[OTRAssets resourcesBundle] compatibleWithTraitCollection:nil];
self.doneBarButtonItem = [[UIBarButtonItem alloc] initWithImage:checkImage style:UIBarButtonItemStylePlain target:self action:@selector(doneButtonPressed:)];
self.groupBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:GROUP_CHAT_STRING() style:UIBarButtonItemStylePlain target:self action:@selector(groupButtonPressed:)];
self.navigationItem.leftBarButtonItem = cancelBarButtonItem;
self.navigationItem.rightBarButtonItem = self.groupBarButtonItem;
_inboxArchiveControl = [[UISegmentedControl alloc] initWithItems:@[ACTIVE_BUDDIES_STRING(), ARCHIVE_STRING()]];
_inboxArchiveControl.selectedSegmentIndex = 0;
[self updateInboxArchiveFilteringAndShowArchived:NO];
[_inboxArchiveControl addTarget:self action:@selector(inboxArchiveControlValueChanged:) forControlEvents:UIControlEventValueChanged];
self.navigationItem.titleView = _inboxArchiveControl;
/////////// TableView ///////////
self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
self.tableView.translatesAutoresizingMaskIntoConstraints = NO;
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.rowHeight = OTRBuddyInfoCellHeight;
[self.view addSubview:self.tableView];
self.tableViewHeader = [[OTRVerticalStackView alloc] init];
[self.tableViewHeader setBackgroundColor:UIColor.groupTableViewBackgroundColor];
self.tableView.tableHeaderView = self.tableViewHeader;
// Add the "Add friends" button
UITableViewCell *cellAddFriends = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cellAddFriends.textLabel.text = ADD_BUDDY_STRING();
cellAddFriends.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
__weak typeof(self)weakSelf = self;
[self.tableViewHeader addStackedSubview:cellAddFriends identifier:ADD_BUDDY_STRING() gravity:OTRVerticalStackViewGravityMiddle height:80 callback:^() {
// TODO: we should migrate to a persistent queue so when
// you add a buddy offline it will eventually work
// See: https://github.com/ChatSecure/ChatSecure-iOS/issues/679
NSArray *accounts = [OTRAccountsManager allAccounts];
__strong typeof(weakSelf)strongSelf = weakSelf;
[strongSelf addBuddy:accounts];
}];
// Add the "Join Group" button
UITableViewCell *cellJoinGroup = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cellJoinGroup.textLabel.text = JOIN_GROUP_STRING();
cellJoinGroup.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[self.tableViewHeader addStackedSubview:cellJoinGroup identifier:JOIN_GROUP_STRING() gravity:OTRVerticalStackViewGravityBottom height:80 callback:^() {
__strong typeof(weakSelf)strongSelf = weakSelf;
[strongSelf joinGroup:cellJoinGroup];
}];
[self.tableView registerClass:[OTRBuddyInfoCell class] forCellReuseIdentifier:[OTRBuddyInfoCell reuseIdentifier]];
[self.tableView autoPinEdgesToSuperviewEdges];
[self setupSearchController];
//////// View Handlers /////////
self.viewHandler = [[OTRYapViewHandler alloc] initWithDatabaseConnection:[OTRDatabaseManager sharedInstance].longLivedReadOnlyConnection databaseChangeNotificationName:[DatabaseNotificationName LongLivedTransactionChanges]];
self.viewHandler.delegate = self;
[self.viewHandler setup:OTRArchiveFilteredBuddiesName groups:@[OTRBuddyGroup]];
self.searchViewHandler = [[OTRYapViewHandler alloc] initWithDatabaseConnection:[OTRDatabaseManager sharedInstance].longLivedReadOnlyConnection databaseChangeNotificationName:[DatabaseNotificationName LongLivedTransactionChanges]];
self.searchViewHandler.delegate = self;
NSString *searchViewName = [YapDatabaseConstants extensionName:DatabaseExtensionNameBuddySearchResultsViewName];
[self.searchViewHandler setup:searchViewName groupBlock:^BOOL(NSString * _Nonnull group, YapDatabaseReadTransaction * _Nonnull transaction) {
return YES;
} sortBlock:^NSComparisonResult(NSString * _Nonnull group1, NSString * _Nonnull group2, YapDatabaseReadTransaction * _Nonnull transaction) {
return [group1 compare:group2];
}];
[self.tableView reloadData];
}
- (void)viewWillAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
[super viewWillAppear:YES];
}
- (void)viewDidAppear:(BOOL)animated
{
[self adjustSearchBarSize];
[super viewDidAppear:YES];
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
// Resize UISearchBar manually - it doesn't do it on its own on device turn.
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
[self adjustSearchBarSize];
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
[self adjustSearchBarSize];
}];
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}
- (void)inboxArchiveControlValueChanged:(id)sender {
if (![sender isKindOfClass:[UISegmentedControl class]]) {
return;
}
UISegmentedControl *segment = sender;
BOOL showArchived = NO;
if (segment.selectedSegmentIndex == 0) {
showArchived = NO;
} else if (segment.selectedSegmentIndex == 1) {
showArchived = YES;
}
[self updateInboxArchiveFilteringAndShowArchived:showArchived];
}
- (YapDatabaseViewFiltering *)getFilteringBlock:(BOOL)showArchived {
YapDatabaseViewFiltering *filtering = [YapDatabaseViewFiltering withObjectBlock:^BOOL(YapDatabaseReadTransaction * _Nonnull transaction, NSString * _Nonnull group, NSString * _Nonnull collection, NSString * _Nonnull key, id _Nonnull object) {
if ([object conformsToProtocol:@protocol(OTRThreadOwner)]) {
id<OTRThreadOwner> threadOwner = object;
BOOL isArchived = threadOwner.isArchived;
return showArchived == isArchived;
}
return YES;
}];
return filtering;
}
- (void) updateInboxArchiveFilteringAndShowArchived:(BOOL)showArchived {
[[OTRDatabaseManager sharedInstance].writeConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
YapDatabaseFilteredViewTransaction *fvt = [transaction ext:OTRArchiveFilteredBuddiesName];
YapDatabaseViewFiltering *filtering = [self getFilteringBlock:showArchived];
[fvt setFiltering:filtering versionTag:[NSUUID UUID].UUIDString];
}];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)setupSearchController {
UITableViewController *searchResultsController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
[searchResultsController.tableView registerClass:[OTRBuddyInfoCell class] forCellReuseIdentifier:[OTRBuddyInfoCell reuseIdentifier]];
searchResultsController.tableView.dataSource = self;
searchResultsController.tableView.delegate = self;
searchResultsController.tableView.estimatedRowHeight = 120;
searchResultsController.tableView.rowHeight = UITableViewAutomaticDimension;
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.hidesNavigationBarDuringPresentation = NO;
self.searchController.delegate = self;
self.definesPresentationContext = YES;
//self.searchController.searchBar.placeholder = SEARCH_STRING;
self.searchController.searchBar.searchBarStyle = UISearchBarStyleMinimal;
self.searchController.searchBar.delegate = self;
[self.tableViewHeader addStackedSubview:self.searchController.searchBar identifier:nil gravity:OTRVerticalStackViewGravityTop];
}
// Make sure bar stays at the top
- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar {
return UIBarPositionTop;
}
- (BOOL)isSearchResultsControllerTableView:(UITableView *)tableView
{
UITableViewController *src = (UITableViewController*)self.searchController.searchResultsController;
if (tableView == src.tableView) {
return YES;
}
return NO;
}
- (OTRYapViewHandler *)viewHandlerForTableView:(UITableView *)tableView {
if ([self isSearchResultsControllerTableView:tableView]) {
return self.searchViewHandler;
}
return self.viewHandler;
}
- (void)cancelButtonPressed:(id)sender
{
// Call dismiss from the split view controller instead
[self.delegate controllerDidCancel:self];
}
- (void)doneButtonPressed:(id)sender
{
__weak __typeof__(self) weakSelf = self;
void (^completion)(NSString *) = ^void(NSString *name) {
__typeof__(self) strongSelf = weakSelf;
[strongSelf completeSelectingBuddies:strongSelf.selectedBuddiesIdSet groupName:name];
};
if (self.selectedBuddiesIdSet.count > 1) {
//Group so need user to select name
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:GROUP_NAME_STRING() message:ENTER_GROUP_NAME_STRING() preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:OK_STRING() style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSString *name = alertController.textFields.firstObject.text;
completion(name);
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:CANCEL_STRING() style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}];
[alertController addAction:okAction];
[alertController addAction:cancelAction];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = GROUP_NAME_STRING();
[[NSNotificationCenter defaultCenter] addObserverForName:UITextFieldTextDidChangeNotification object:textField queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
okAction.enabled = textField.text.length > 0;
}];
}];
[self presentViewController:alertController animated:YES completion:nil];
} else {
completion(nil);
}
}
/** Intended to be called if selecting one buddy or after a group chat is created*/
- (void)completeSelectingBuddies:(NSSet <NSString *>*)buddies groupName:(nullable NSString*)groupName {
if (![self.delegate respondsToSelector:@selector(controller:didSelectBuddies:accountId:name:)]) {
return;
}
//TODO: Naive choosing account just any buddy but should really check that account is connected or show picker
__block NSString *accountId = nil;
[self.viewHandler.databaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
NSString *buddyKey = [buddies anyObject];
accountId = [OTRBuddy fetchObjectWithUniqueID:buddyKey transaction:transaction].accountUniqueId;
}];
if (!accountId) {
DDLogError(@"completeSelectingBuddies error: No account found!");
return;
}
[self.delegate controller:self didSelectBuddies:[buddies allObjects] accountId:accountId name:groupName];
}
- (void) groupButtonPressed:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"OTRComposeGroup" bundle:[OTRAssets resourcesBundle]];
OTRComposeGroupViewController *vc = (OTRComposeGroupViewController *)[storyboard instantiateInitialViewController];
vc.delegate = self;
[self.navigationController pushViewController:vc animated:YES];
}
- (void)switchSelectionMode {
_selectionModeIsSingle = !_selectionModeIsSingle;
// Change from Join Group / Add Buddy
NSIndexPath *path = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationNone];
//Update right bar button item
if (self.selectionModeIsSingle) {
self.navigationItem.rightBarButtonItem = self.groupBarButtonItem;
} else {
self.navigationItem.rightBarButtonItem = self.doneBarButtonItem;
self.navigationItem.rightBarButtonItem.enabled = NO;
}
}
- (id<OTRThreadOwner>)threadOwnerAtIndexPath:(NSIndexPath *)indexPath withTableView:(UITableView *)tableView
{
NSIndexPath *viewIndexPath = [NSIndexPath indexPathForItem:indexPath.row inSection:0];
OTRYapViewHandler *viewHandler = [self viewHandlerForTableView:tableView];
return [viewHandler object:viewIndexPath];
}
- (void)selectedThreadOwner:(NSString *)buddyId{
if (![buddyId length]) {
return;
}
if ([self.selectedBuddiesIdSet containsObject:buddyId]) {
[self.selectedBuddiesIdSet removeObject:buddyId];
} else {
[self.selectedBuddiesIdSet addObject:buddyId];
}
//Check legth of selected buddies and if none then disable button
if ([self.selectedBuddiesIdSet count]) {
self.navigationItem.rightBarButtonItem.enabled = YES;
} else {
[self switchSelectionMode];
}
}
#pragma - mark keyBoardAnimation Methods
- (void)keyboardWillShow:(NSNotification *)notification
{
[self animateTableViewWithKeyboardNotification:notification];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
[self animateTableViewWithKeyboardNotification:notification];
}
- (void)animateTableViewWithKeyboardNotification:(NSNotification *)notification
{
NSDictionary *userInfo = notification.userInfo;
//
// Get keyboard size.
NSValue *endFrameValue = userInfo[UIKeyboardFrameEndUserInfoKey];
CGRect keyboardEndFrame = [self.view convertRect:endFrameValue.CGRectValue fromView:nil];
//
// Get keyboard animation.
NSNumber *durationValue = userInfo[UIKeyboardAnimationDurationUserInfoKey];
NSTimeInterval animationDuration = durationValue.doubleValue;
NSNumber *curveValue = userInfo[UIKeyboardAnimationCurveUserInfoKey];
UIViewAnimationCurve animationCurve = curveValue.intValue;
CGFloat viewHeight = CGRectGetMaxY(self.view.frame);
CGFloat keyboardY = CGRectGetMinY(keyboardEndFrame);
CGFloat height = viewHeight - keyboardY;
//If height is less than 0 or it's hiding set to 0
if ([notification.name isEqualToString:UIKeyboardWillHideNotification] || height < 0) {
height = 0;
}
[self animateTableViewToKeyboardHeight:height animationCurve:animationCurve animationDuration:animationDuration];
}
- (void)animateTableViewToKeyboardHeight:(CGFloat)keyBoardHeight animationCurve:(UIViewAnimationCurve)animationCurve animationDuration:(NSTimeInterval)animationDuration
{
self.tableViewBottomConstraint.constant = -keyBoardHeight;
void (^animations)() = ^() {
[self.view layoutIfNeeded];
};
[UIView animateWithDuration:animationDuration
delay:0.0
options:(animationCurve << 16)
animations:animations
completion:nil];
}
#pragma - mark UITableViewDataSource Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
OTRYapViewHandler *viewHandler = [self viewHandlerForTableView:tableView];
return [viewHandler.mappings numberOfSections];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
OTRYapViewHandler *viewHandler = [self viewHandlerForTableView:tableView];
return [viewHandler.mappings numberOfItemsInSection:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
OTRBuddyInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:[OTRBuddyInfoCell reuseIdentifier] forIndexPath:indexPath];
id<OTRThreadOwner> threadOwner = [self threadOwnerAtIndexPath:indexPath withTableView:tableView];
__block OTRAccount *account = nil;
[OTRDatabaseManager.shared.uiConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
BOOL showAccount = [self shouldShowAccountLabelWithTransaction:transaction];
if (showAccount) {
account = [OTRAccount accountForThread:threadOwner transaction:transaction];
}
}];
[cell setThread:threadOwner account:account];
if ([self.selectedBuddiesIdSet containsObject:[threadOwner threadIdentifier]]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
/** By default will show account if numAccounts > 0*/
- (BOOL) shouldShowAccountLabelWithTransaction:(YapDatabaseReadTransaction*)transaction {
NSUInteger numberOfAccounts = 0;
numberOfAccounts = [OTRAccount numberOfAccountsWithTransaction:transaction];
if (numberOfAccounts > 1) {
return YES;
}
return NO;
}
#pragma - mark UITableViewDelegate Methods
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return OTRBuddyInfoCellHeight;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return OTRBuddyInfoCellHeight;
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleDelete;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
id<OTRThreadOwner> threadOwner = [self threadOwnerAtIndexPath:indexPath withTableView:tableView];
if (self.selectionModeIsSingle == YES) {
NSSet <NSString *>*buddySet = [NSSet setWithObject:[threadOwner threadIdentifier]];
[self completeSelectingBuddies:buddySet groupName:nil];
} else {
[self selectedThreadOwner:[threadOwner threadIdentifier]];
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
- (nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
NSIndexPath *databaseIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:0];
id <OTRThreadOwner> thread = [self threadOwnerAtIndexPath:databaseIndexPath withTableView:tableView];
if (!thread) { return nil; }
return [UITableView editActionsForThread:thread deleteActionAlsoRemovesFromRoster:YES connection:OTRDatabaseManager.shared.writeConnection];
}
- (void)addBuddy:(NSArray *)accountsAbleToAddBuddies
{
if ([accountsAbleToAddBuddies count] == 0) {
return; // No accounts
}
//add buddy cell
UIViewController *viewController = nil;
if([accountsAbleToAddBuddies count] > 1) {
// pick which account
OTRChooseAccountViewController *chooser = [[OTRChooseAccountViewController alloc] init];
chooser.selectionBlock = ^(OTRChooseAccountViewController * _Nonnull chooseVC, OTRAccount * _Nonnull account) {
OTRNewBuddyViewController *newBuddyVC = [[OTRNewBuddyViewController alloc] initWithAccountId:account.uniqueId];
[chooseVC.navigationController pushViewController:newBuddyVC animated:YES];
};
viewController = chooser;
}
else {
OTRAccount *account = [accountsAbleToAddBuddies firstObject];
viewController = [[OTRNewBuddyViewController alloc] initWithAccountId:account.uniqueId];
}
[self.navigationController pushViewController:viewController animated:YES];
}
- (void) joinGroup:(id)sender {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:JOIN_GROUP_STRING() message:nil preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"room@conference.example.com";
}];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = [NSString stringWithFormat:@"%@ (%@)", PASSWORD_STRING(), OPTIONAL_STRING()];
}];
UIAlertAction *joinAction = [UIAlertAction actionWithTitle:OK_STRING() style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSString *jidStr = alertController.textFields.firstObject.text;
NSString *pass = alertController.textFields.lastObject.text;
if (!jidStr.length) {
return;
}
XMPPJID *roomJid = [XMPPJID jidWithString:jidStr];
if (!roomJid) {
return;
}
NSArray *accounts = [OTRAccountsManager allAccounts];
void (^joinRoom)(OTRAccount *account) = ^void(OTRAccount *account) {
OTRXMPPManager *xmpp = (OTRXMPPManager*)[OTRProtocolManager.shared protocolForAccount:account];
if (!xmpp) { return; }
[xmpp.roomManager joinRoom:roomJid withNickname:account.displayName subject:nil password:pass];
[self dismissViewControllerAnimated:YES completion:nil];
};
if (accounts.count > 1) {
OTRChooseAccountViewController *chooser = [[OTRChooseAccountViewController alloc] init];
chooser.selectionBlock = ^(OTRChooseAccountViewController * _Nonnull chooseVC, OTRAccount * _Nonnull account) {
[chooseVC dismissViewControllerAnimated:YES completion:nil];
joinRoom(account);
};
[self.navigationController pushViewController:chooser animated:YES];
} else {
joinRoom(accounts.firstObject);
}
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:CANCEL_STRING() style:UIAlertActionStyleCancel handler:nil];
[alertController addAction:joinAction];
[alertController addAction:cancel];
[self presentViewController:alertController animated:YES completion:nil];
}
#pragma - mark UIScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self.view endEditing:YES];
}
#pragma - mark YapDatabaseViewUpdate
- (void)didSetupMappings:(OTRYapViewHandler *)handler
{
UITableView *tableView = nil;
if (self.searchViewHandler == handler) {
tableView = ((UITableViewController*)self.searchController.searchResultsController).tableView;
} else if (self.viewHandler == handler) {
tableView = self.tableView;
}
[tableView reloadData];
}
- (void)didReceiveChanges:(OTRYapViewHandler *)handler sectionChanges:(NSArray<YapDatabaseViewSectionChange *> *)sectionChanges rowChanges:(NSArray<YapDatabaseViewRowChange *> *)rowChanges
{
if ([sectionChanges count] == 0 && [rowChanges count] == 0)
{
// Nothing has changed that affects our tableView
return;
}
UITableView *tableView = self.tableView;
if (self.searchViewHandler == handler) {
tableView = ((UITableViewController*)self.searchController.searchResultsController).tableView;
}
[tableView beginUpdates];
for (YapDatabaseViewSectionChange *sectionChange in sectionChanges)
{
switch (sectionChange.type)
{
case YapDatabaseViewChangeDelete :
{
[tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionChange.index]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
case YapDatabaseViewChangeInsert :
{
[tableView insertSections:[NSIndexSet indexSetWithIndex:sectionChange.index]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
case YapDatabaseViewChangeUpdate:
case YapDatabaseViewChangeMove:
break;
}
}
for (YapDatabaseViewRowChange *rowChange in rowChanges)
{
switch (rowChange.type)
{
case YapDatabaseViewChangeDelete :
{
[tableView deleteRowsAtIndexPaths:@[ rowChange.indexPath ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
case YapDatabaseViewChangeInsert :
{
[tableView insertRowsAtIndexPaths:@[ rowChange.newIndexPath ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
case YapDatabaseViewChangeMove :
{
[tableView deleteRowsAtIndexPaths:@[ rowChange.indexPath ]
withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:@[ rowChange.newIndexPath ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
case YapDatabaseViewChangeUpdate :
{
[tableView reloadRowsAtIndexPaths:@[ rowChange.indexPath ]
withRowAnimation:UITableViewRowAnimationNone];
break;
}
}
}
[tableView endUpdates];
}
#pragma - mark UISearchResultsUpdating
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
NSString *searchString = [self.searchController.searchBar text];
if ([searchString length]) {
searchString = [NSString stringWithFormat:@"%@*",searchString];
[self.searchQueue enqueueQuery:searchString];
[self.searchConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
NSString *searchViewName = [YapDatabaseConstants extensionName:DatabaseExtensionNameBuddySearchResultsViewName];
[[transaction ext:searchViewName] performSearchWithQueue:self.searchQueue];
}];
}
}
#pragma - mark UISearchControllerDelegate
- (void)willDismissSearchController:(UISearchController *)searchController
{
//Make sure all the checkmarks are correctly updated
[self.tableView reloadData];
}
- (void)didDismissSearchController:(UISearchController *)searchController
{
// Needs to be done, when device was rotated and search bar had focus.
// The search bar will be in the wrong size now, otherwise.
[self adjustSearchBarSize];
}
#pragma - mark OTRComposeGroupViewControllerDelegate
- (void)groupBuddiesSelected:(OTRComposeGroupViewController *)composeViewController buddyUniqueIds:(NSArray<NSString *> *)buddyUniqueIds groupName:(NSString *)groupName {
[self completeSelectingBuddies:[NSSet setWithArray:buddyUniqueIds] groupName:groupName];
}
- (void)groupSelectionCancelled:(OTRComposeGroupViewController *)composeViewController {
}
#pragma mark internal methods
/**
* Resize our UISearchBar, so it fills its superview, exactly.
*/
- (void)adjustSearchBarSize {
UISearchBar *searchBar = self.searchController.searchBar;
CGRect frame = searchBar.superview.bounds;
searchBar.frame = frame;
}
@end