2018-01-08 18:17:29 +00:00
|
|
|
|
/*
|
|
|
|
|
Copyright 2017 Vector Creations Ltd
|
|
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
|
limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#import "GroupParticipantsViewController.h"
|
|
|
|
|
|
|
|
|
|
#import "AppDelegate.h"
|
2018-11-30 14:56:10 +00:00
|
|
|
|
#import "Riot-Swift.h"
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
#import "Contact.h"
|
|
|
|
|
#import "ContactTableViewCell.h"
|
|
|
|
|
|
|
|
|
|
#import "RageShakeManager.h"
|
|
|
|
|
|
|
|
|
|
@interface GroupParticipantsViewController ()
|
|
|
|
|
{
|
|
|
|
|
// Search result
|
|
|
|
|
NSString *currentSearchText;
|
|
|
|
|
NSMutableArray<Contact*> *filteredActualParticipants;
|
|
|
|
|
NSMutableArray<Contact*> *filteredInvitedParticipants;
|
|
|
|
|
|
|
|
|
|
// Mask view while processing a request
|
|
|
|
|
UIActivityIndicatorView *pendingMaskSpinnerView;
|
|
|
|
|
|
2018-01-16 22:49:04 +00:00
|
|
|
|
// The current pushed view controller
|
|
|
|
|
UIViewController *pushedViewController;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
// Display a gradient view above the screen.
|
|
|
|
|
CAGradientLayer* tableViewMaskLayer;
|
|
|
|
|
|
|
|
|
|
// Display a button to invite new member.
|
|
|
|
|
UIImageView* addParticipantButtonImageView;
|
|
|
|
|
NSLayoutConstraint *addParticipantButtonImageViewBottomConstraint;
|
|
|
|
|
|
|
|
|
|
UIAlertController *currentAlert;
|
|
|
|
|
|
2019-01-11 09:32:56 +00:00
|
|
|
|
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
|
|
|
|
|
id kThemeServiceDidChangeThemeNotificationObserver;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation GroupParticipantsViewController
|
|
|
|
|
|
|
|
|
|
#pragma mark - Class methods
|
|
|
|
|
|
|
|
|
|
+ (UINib *)nib
|
|
|
|
|
{
|
|
|
|
|
return [UINib nibWithNibName:NSStringFromClass(self.class)
|
|
|
|
|
bundle:[NSBundle bundleForClass:self.class]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (instancetype)groupParticipantsViewController
|
|
|
|
|
{
|
|
|
|
|
return [[[self class] alloc] initWithNibName:NSStringFromClass(self.class)
|
|
|
|
|
bundle:[NSBundle bundleForClass:self.class]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
- (void)finalizeInit
|
|
|
|
|
{
|
|
|
|
|
[super finalizeInit];
|
|
|
|
|
|
|
|
|
|
// Setup `MXKViewControllerHandling` properties
|
|
|
|
|
self.enableBarTintColorStatusChange = NO;
|
|
|
|
|
self.rageShakeManager = [RageShakeManager sharedManager];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)viewDidLoad
|
|
|
|
|
{
|
|
|
|
|
[super viewDidLoad];
|
|
|
|
|
// Do any additional setup after loading the view, typically from a nib.
|
|
|
|
|
|
|
|
|
|
// Check whether the view controller has been pushed via storyboard
|
|
|
|
|
if (!self.tableView)
|
|
|
|
|
{
|
|
|
|
|
// Instantiate view controller objects
|
|
|
|
|
[[[self class] nib] instantiateWithOwner:self options:nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Adjust Top and Bottom constraints to take into account potential navBar and tabBar.
|
|
|
|
|
[NSLayoutConstraint deactivateConstraints:@[_searchBarTopConstraint, _tableViewBottomConstraint]];
|
|
|
|
|
|
|
|
|
|
_searchBarTopConstraint = [NSLayoutConstraint constraintWithItem:self.topLayoutGuide
|
|
|
|
|
attribute:NSLayoutAttributeBottom
|
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
|
toItem:self.searchBarHeader
|
|
|
|
|
attribute:NSLayoutAttributeTop
|
|
|
|
|
multiplier:1.0f
|
|
|
|
|
constant:0.0f];
|
|
|
|
|
|
|
|
|
|
_tableViewBottomConstraint = [NSLayoutConstraint constraintWithItem:self.bottomLayoutGuide
|
|
|
|
|
attribute:NSLayoutAttributeTop
|
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
|
toItem:self.tableView
|
|
|
|
|
attribute:NSLayoutAttributeBottom
|
|
|
|
|
multiplier:1.0f
|
|
|
|
|
constant:0.0f];
|
|
|
|
|
|
|
|
|
|
[NSLayoutConstraint activateConstraints:@[_searchBarTopConstraint, _tableViewBottomConstraint]];
|
|
|
|
|
|
|
|
|
|
_searchBarView.placeholder = NSLocalizedStringFromTable(@"group_participants_filter_members", @"Vector", nil);
|
|
|
|
|
_searchBarView.returnKeyType = UIReturnKeyDone;
|
|
|
|
|
_searchBarView.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
|
|
|
|
|
|
|
|
|
// Search bar header is hidden when no group is provided
|
|
|
|
|
_searchBarHeader.hidden = (self.group == nil);
|
|
|
|
|
|
|
|
|
|
// Enable self-sizing cells and section headers.
|
|
|
|
|
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
|
|
|
|
self.tableView.estimatedRowHeight = 74;
|
|
|
|
|
self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension;
|
|
|
|
|
self.tableView.estimatedSectionHeaderHeight = 30;
|
|
|
|
|
|
|
|
|
|
// Hide line separators of empty cells
|
|
|
|
|
self.tableView.tableFooterView = [[UIView alloc] init];
|
|
|
|
|
|
|
|
|
|
[self.tableView registerClass:ContactTableViewCell.class forCellReuseIdentifier:@"ParticipantTableViewCellId"];
|
|
|
|
|
[self.tableView registerNib:MXKTableViewHeaderFooterWithLabel.nib forHeaderFooterViewReuseIdentifier:MXKTableViewHeaderFooterWithLabel.defaultReuseIdentifier];
|
|
|
|
|
|
|
|
|
|
// @TODO: Add programmatically the button to add participant.
|
|
|
|
|
//[self addAddParticipantButton];
|
|
|
|
|
|
|
|
|
|
// Observe user interface theme change.
|
2019-01-11 09:32:56 +00:00
|
|
|
|
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
[self userInterfaceThemeDidChange];
|
|
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
[self userInterfaceThemeDidChange];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)userInterfaceThemeDidChange
|
|
|
|
|
{
|
2019-01-11 10:45:27 +00:00
|
|
|
|
[ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar];
|
2018-11-29 16:31:41 +00:00
|
|
|
|
|
2019-01-11 10:45:27 +00:00
|
|
|
|
self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
[self refreshSearchBarItemsColor:_searchBarView];
|
|
|
|
|
|
2019-01-11 10:45:27 +00:00
|
|
|
|
_searchBarHeaderBorder.backgroundColor = ThemeService.shared.theme.headerBorderColor;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
// Check the table view style to select its bg color.
|
2019-01-11 10:45:27 +00:00
|
|
|
|
self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor);
|
2018-01-08 18:17:29 +00:00
|
|
|
|
self.view.backgroundColor = self.tableView.backgroundColor;
|
2019-02-18 11:53:13 +00:00
|
|
|
|
self.tableView.separatorColor = ThemeService.shared.theme.lineBreakColor;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
// Update the gradient view above the screen
|
|
|
|
|
CGFloat white = 1.0;
|
2019-01-11 10:45:27 +00:00
|
|
|
|
[ThemeService.shared.theme.backgroundColor getWhite:&white alpha:nil];
|
2018-01-08 18:17:29 +00:00
|
|
|
|
CGColorRef opaqueWhiteColor = [UIColor colorWithWhite:white alpha:1.0].CGColor;
|
|
|
|
|
CGColorRef transparentWhiteColor = [UIColor colorWithWhite:white alpha:0].CGColor;
|
2019-01-07 23:24:11 +00:00
|
|
|
|
tableViewMaskLayer.colors = @[(__bridge id) transparentWhiteColor, (__bridge id) transparentWhiteColor, (__bridge id) opaqueWhiteColor];
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
if (self.tableView.dataSource)
|
|
|
|
|
{
|
|
|
|
|
[self.tableView reloadData];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (UIStatusBarStyle)preferredStatusBarStyle
|
|
|
|
|
{
|
2019-01-11 10:45:27 +00:00
|
|
|
|
return ThemeService.shared.theme.statusBarStyle;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This method is called when the viewcontroller is added or removed from a container view controller.
|
|
|
|
|
- (void)didMoveToParentViewController:(nullable UIViewController *)parent
|
|
|
|
|
{
|
|
|
|
|
[super didMoveToParentViewController:parent];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)destroy
|
|
|
|
|
{
|
2018-01-16 22:49:04 +00:00
|
|
|
|
// Release the potential pushed view controller
|
|
|
|
|
[self releasePushedViewController];
|
|
|
|
|
|
2019-01-11 09:32:56 +00:00
|
|
|
|
if (kThemeServiceDidChangeThemeNotificationObserver)
|
2018-01-08 18:17:29 +00:00
|
|
|
|
{
|
2019-01-11 09:32:56 +00:00
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver];
|
|
|
|
|
kThemeServiceDidChangeThemeNotificationObserver = nil;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (currentAlert)
|
|
|
|
|
{
|
|
|
|
|
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
|
|
|
|
currentAlert = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_group = nil;
|
|
|
|
|
_mxSession = nil;
|
|
|
|
|
|
|
|
|
|
filteredActualParticipants = nil;
|
|
|
|
|
filteredInvitedParticipants = nil;
|
|
|
|
|
|
|
|
|
|
actualParticipants = nil;
|
|
|
|
|
invitedParticipants = nil;
|
|
|
|
|
|
|
|
|
|
[self removePendingActionMask];
|
|
|
|
|
|
|
|
|
|
// Note: all observers are removed during super call.
|
|
|
|
|
[super destroy];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)viewWillAppear:(BOOL)animated
|
|
|
|
|
{
|
|
|
|
|
[super viewWillAppear:animated];
|
|
|
|
|
|
|
|
|
|
// Screen tracking
|
2018-06-27 07:55:06 +00:00
|
|
|
|
[[Analytics sharedInstance] trackScreen:@"GroupDetailsPeople"];
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
2018-01-16 22:49:04 +00:00
|
|
|
|
// Release the potential pushed view controller
|
|
|
|
|
[self releasePushedViewController];
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
if (_group)
|
|
|
|
|
{
|
|
|
|
|
// Restore the listeners on the group update.
|
|
|
|
|
[self registerOnGroupChangeNotifications];
|
|
|
|
|
|
|
|
|
|
// Check whether the selected group is stored in the user's session, or if it is a group preview.
|
|
|
|
|
// Replace the displayed group instance with the one stored in the session (if any).
|
|
|
|
|
MXGroup *storedGroup = [_mxSession groupWithGroupId:_group.groupId];
|
|
|
|
|
BOOL isPreview = (!storedGroup);
|
|
|
|
|
|
|
|
|
|
// Force refresh
|
|
|
|
|
[self refreshDisplayWithGroup:(isPreview ? _group : storedGroup)];
|
|
|
|
|
|
|
|
|
|
// Prepare a block called on successful update in case of a group preview.
|
|
|
|
|
// Indeed the group update notifications are triggered by the matrix session only for the user's groups.
|
|
|
|
|
void (^success)(void) = ^void(void)
|
|
|
|
|
{
|
|
|
|
|
[self refreshDisplayWithGroup:_group];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Trigger a refresh on the group members and the invited users.
|
|
|
|
|
[self.mxSession updateGroupUsers:_group success:(isPreview ? success : nil) failure:^(NSError *error) {
|
|
|
|
|
|
|
|
|
|
NSLog(@"[GroupParticipantsViewController] viewWillAppear: group members update failed %@", _group.groupId);
|
|
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
[self.mxSession updateGroupInvitedUsers:_group success:(isPreview ? success : nil) failure:^(NSError *error) {
|
|
|
|
|
|
|
|
|
|
NSLog(@"[GroupParticipantsViewController] viewWillAppear: invited users update failed %@", _group.groupId);
|
|
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)viewWillDisappear:(BOOL)animated
|
|
|
|
|
{
|
|
|
|
|
[super viewWillDisappear:animated];
|
|
|
|
|
|
|
|
|
|
[self cancelRegistrationOnGroupChangeNotifications];
|
|
|
|
|
|
|
|
|
|
if (currentAlert)
|
|
|
|
|
{
|
|
|
|
|
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
|
|
|
|
currentAlert = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// cancel any pending search
|
|
|
|
|
[self searchBarCancelButtonClicked:_searchBarView];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)withdrawViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion
|
|
|
|
|
{
|
|
|
|
|
// Check whether the current view controller is displayed inside a segmented view controller in order to withdraw the right item
|
|
|
|
|
if (self.parentViewController && [self.parentViewController isKindOfClass:SegmentedViewController.class])
|
|
|
|
|
{
|
|
|
|
|
[((SegmentedViewController*)self.parentViewController) withdrawViewControllerAnimated:animated completion:completion];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[super withdrawViewControllerAnimated:animated completion:completion];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)viewDidLayoutSubviews
|
|
|
|
|
{
|
|
|
|
|
[super viewDidLayoutSubviews];
|
|
|
|
|
|
|
|
|
|
// Sanity check
|
|
|
|
|
if (tableViewMaskLayer)
|
|
|
|
|
{
|
|
|
|
|
CGRect currentBounds = tableViewMaskLayer.bounds;
|
|
|
|
|
CGRect newBounds = CGRectIntegral(self.view.frame);
|
|
|
|
|
|
|
|
|
|
newBounds.size.height -= self.keyboardHeight;
|
|
|
|
|
|
|
|
|
|
// Check if there is an update
|
|
|
|
|
if (!CGSizeEqualToSize(currentBounds.size, newBounds.size))
|
|
|
|
|
{
|
|
|
|
|
newBounds.origin = CGPointZero;
|
|
|
|
|
|
|
|
|
|
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn
|
|
|
|
|
animations:^{
|
|
|
|
|
|
|
|
|
|
tableViewMaskLayer.bounds = newBounds;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
completion:^(BOOL finished){
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hide the addParticipants button on landscape when keyboard is visible
|
|
|
|
|
BOOL isLandscapeOriented = UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
|
|
|
|
|
addParticipantButtonImageView.hidden = tableViewMaskLayer.hidden = (isLandscapeOriented && self.keyboardHeight);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setGroup:(MXGroup*)group withMatrixSession:(MXSession*)mxSession
|
|
|
|
|
{
|
|
|
|
|
// Cancel any pending search
|
|
|
|
|
[self searchBarCancelButtonClicked:_searchBarView];
|
|
|
|
|
|
|
|
|
|
if (_mxSession != mxSession)
|
|
|
|
|
{
|
|
|
|
|
[self cancelRegistrationOnGroupChangeNotifications];
|
|
|
|
|
_mxSession = mxSession;
|
|
|
|
|
|
|
|
|
|
[self registerOnGroupChangeNotifications];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self addMatrixSession:mxSession];
|
|
|
|
|
|
|
|
|
|
[self refreshDisplayWithGroup:group];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
- (void)registerOnGroupChangeNotifications
|
|
|
|
|
{
|
|
|
|
|
if (_mxSession)
|
|
|
|
|
{
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateGroupUsers:) name:kMXSessionDidUpdateGroupUsersNotification object:_mxSession];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)cancelRegistrationOnGroupChangeNotifications
|
|
|
|
|
{
|
|
|
|
|
// Remove any pending observers
|
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionDidUpdateGroupUsersNotification object:_mxSession];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)didUpdateGroupUsers:(NSNotification *)notif
|
|
|
|
|
{
|
|
|
|
|
MXGroup *group = notif.userInfo[kMXSessionNotificationGroupKey];
|
|
|
|
|
if (group && [group.groupId isEqualToString:_group.groupId])
|
|
|
|
|
{
|
|
|
|
|
// Update the current displayed group instance with the one stored in the session
|
|
|
|
|
[self refreshDisplayWithGroup:group];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)refreshDisplayWithGroup:(MXGroup *)group
|
|
|
|
|
{
|
|
|
|
|
_group = group;
|
|
|
|
|
|
|
|
|
|
if (_group)
|
|
|
|
|
{
|
|
|
|
|
_searchBarHeader.hidden = NO;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Search bar header is hidden when no group is provided
|
|
|
|
|
_searchBarHeader.hidden = YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Refresh the members list.
|
|
|
|
|
[self refreshParticipantsList];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)startActivityIndicator
|
|
|
|
|
{
|
|
|
|
|
// Check whether the current view controller is displayed inside a segmented view controller in order to run the right activity view
|
|
|
|
|
if (self.parentViewController && [self.parentViewController isKindOfClass:SegmentedViewController.class])
|
|
|
|
|
{
|
|
|
|
|
[((SegmentedViewController*)self.parentViewController) startActivityIndicator];
|
|
|
|
|
|
|
|
|
|
// Force stop the activity view of the view controller
|
|
|
|
|
[self.activityIndicator stopAnimating];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[super startActivityIndicator];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)stopActivityIndicator
|
|
|
|
|
{
|
|
|
|
|
// Check whether the current view controller is displayed inside a segmented view controller in order to stop the right activity view
|
|
|
|
|
if (self.parentViewController && [self.parentViewController isKindOfClass:SegmentedViewController.class])
|
|
|
|
|
{
|
|
|
|
|
[((SegmentedViewController*)self.parentViewController) stopActivityIndicator];
|
|
|
|
|
|
|
|
|
|
// Force stop the activity view of the view controller
|
|
|
|
|
[self.activityIndicator stopAnimating];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[super stopActivityIndicator];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setKeyboardHeight:(CGFloat)keyboardHeight
|
|
|
|
|
{
|
|
|
|
|
super.keyboardHeight = keyboardHeight;
|
|
|
|
|
|
|
|
|
|
// Update addParticipants button position with animation
|
|
|
|
|
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn
|
|
|
|
|
animations:^{
|
|
|
|
|
|
|
|
|
|
addParticipantButtonImageViewBottomConstraint.constant = keyboardHeight + 9;
|
|
|
|
|
|
|
|
|
|
// Force to render the view
|
|
|
|
|
[self.view layoutIfNeeded];
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
completion:^(BOOL finished){
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - Internals
|
|
|
|
|
|
|
|
|
|
- (void)refreshTableView
|
|
|
|
|
{
|
|
|
|
|
[self.tableView reloadData];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)addAddParticipantButton
|
|
|
|
|
{
|
|
|
|
|
// Add blur mask programmatically
|
|
|
|
|
tableViewMaskLayer = [CAGradientLayer layer];
|
|
|
|
|
|
2019-01-11 10:45:27 +00:00
|
|
|
|
// Consider the grayscale components of the ThemeService.shared.theme.backgroundColor.
|
2018-01-08 18:17:29 +00:00
|
|
|
|
CGFloat white = 1.0;
|
2019-01-11 10:45:27 +00:00
|
|
|
|
[ThemeService.shared.theme.backgroundColor getWhite:&white alpha:nil];
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
CGColorRef opaqueWhiteColor = [UIColor colorWithWhite:white alpha:1.0].CGColor;
|
|
|
|
|
CGColorRef transparentWhiteColor = [UIColor colorWithWhite:white alpha:0].CGColor;
|
|
|
|
|
|
2019-01-07 23:24:11 +00:00
|
|
|
|
tableViewMaskLayer.colors = @[(__bridge id) transparentWhiteColor, (__bridge id) transparentWhiteColor, (__bridge id) opaqueWhiteColor];
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
// display a gradient to the rencents bottom (20% of the bottom of the screen)
|
2019-01-07 23:24:11 +00:00
|
|
|
|
tableViewMaskLayer.locations = @[@0.0F,
|
|
|
|
|
@0.85F,
|
|
|
|
|
@1.0F];
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
tableViewMaskLayer.bounds = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
|
|
|
|
|
tableViewMaskLayer.anchorPoint = CGPointZero;
|
|
|
|
|
|
|
|
|
|
// CAConstraint is not supported on IOS.
|
|
|
|
|
// it seems only being supported on Mac OS.
|
|
|
|
|
// so viewDidLayoutSubviews will refresh the layout bounds.
|
|
|
|
|
[self.view.layer addSublayer:tableViewMaskLayer];
|
|
|
|
|
|
|
|
|
|
// Add + button
|
|
|
|
|
addParticipantButtonImageView = [[UIImageView alloc] init];
|
|
|
|
|
[addParticipantButtonImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
|
|
|
|
|
[self.view addSubview:addParticipantButtonImageView];
|
|
|
|
|
|
|
|
|
|
addParticipantButtonImageView.backgroundColor = [UIColor clearColor];
|
|
|
|
|
addParticipantButtonImageView.contentMode = UIViewContentModeCenter;
|
|
|
|
|
addParticipantButtonImageView.image = [UIImage imageNamed:@"add_group_participant"];
|
|
|
|
|
|
|
|
|
|
CGFloat side = 78.0f;
|
|
|
|
|
NSLayoutConstraint* widthConstraint = [NSLayoutConstraint constraintWithItem:addParticipantButtonImageView
|
|
|
|
|
attribute:NSLayoutAttributeWidth
|
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
|
toItem:nil
|
|
|
|
|
attribute:NSLayoutAttributeNotAnAttribute
|
|
|
|
|
multiplier:1
|
|
|
|
|
constant:side];
|
|
|
|
|
|
|
|
|
|
NSLayoutConstraint* heightConstraint = [NSLayoutConstraint constraintWithItem:addParticipantButtonImageView
|
|
|
|
|
attribute:NSLayoutAttributeHeight
|
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
|
toItem:nil
|
|
|
|
|
attribute:NSLayoutAttributeNotAnAttribute
|
|
|
|
|
multiplier:1
|
|
|
|
|
constant:side];
|
|
|
|
|
|
|
|
|
|
NSLayoutConstraint* centerXConstraint = [NSLayoutConstraint constraintWithItem:addParticipantButtonImageView
|
|
|
|
|
attribute:NSLayoutAttributeCenterX
|
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
|
toItem:self.view
|
|
|
|
|
attribute:NSLayoutAttributeCenterX
|
|
|
|
|
multiplier:1
|
|
|
|
|
constant:0];
|
|
|
|
|
|
|
|
|
|
addParticipantButtonImageViewBottomConstraint = [NSLayoutConstraint constraintWithItem:self.view
|
|
|
|
|
attribute:NSLayoutAttributeBottom
|
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
|
toItem:addParticipantButtonImageView
|
|
|
|
|
attribute:NSLayoutAttributeBottom
|
|
|
|
|
multiplier:1
|
|
|
|
|
constant:self.keyboardHeight + 9];
|
|
|
|
|
|
|
|
|
|
// Available on iOS 8 and later
|
|
|
|
|
[NSLayoutConstraint activateConstraints:@[widthConstraint, heightConstraint, centerXConstraint, addParticipantButtonImageViewBottomConstraint]];
|
|
|
|
|
|
|
|
|
|
addParticipantButtonImageView.userInteractionEnabled = YES;
|
|
|
|
|
|
|
|
|
|
// Handle tap gesture
|
|
|
|
|
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onAddParticipantButtonPressed)];
|
|
|
|
|
[tap setNumberOfTouchesRequired:1];
|
|
|
|
|
[tap setNumberOfTapsRequired:1];
|
|
|
|
|
[tap setDelegate:self];
|
|
|
|
|
[addParticipantButtonImageView addGestureRecognizer:tap];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)onAddParticipantButtonPressed
|
|
|
|
|
{
|
|
|
|
|
// Push the contacts picker.
|
2018-01-16 22:49:04 +00:00
|
|
|
|
ContactsTableViewController *contactsPickerViewController = [ContactsTableViewController contactsTableViewController];
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
// Set delegate to handle action on member (start chat, mention)
|
|
|
|
|
contactsPickerViewController.contactsTableViewControllerDelegate = self;
|
|
|
|
|
|
|
|
|
|
// Prepare its data source
|
|
|
|
|
ContactsDataSource *contactsDataSource = [[ContactsDataSource alloc] initWithMatrixSession:self.mxSession];
|
|
|
|
|
contactsDataSource.areSectionsShrinkable = YES;
|
|
|
|
|
contactsDataSource.displaySearchInputInContactsList = YES;
|
|
|
|
|
contactsDataSource.forceMatrixIdInDisplayName = YES;
|
|
|
|
|
// Add a plus icon to the contact cell in the contacts picker, in order to make it more understandable for the end user.
|
|
|
|
|
contactsDataSource.contactCellAccessoryImage = [UIImage imageNamed:@"plus_icon"];
|
|
|
|
|
|
|
|
|
|
// List all the participants matrix user id to ignore them during the contacts search.
|
|
|
|
|
for (Contact *contact in actualParticipants)
|
|
|
|
|
{
|
2019-01-07 23:24:11 +00:00
|
|
|
|
contactsDataSource.ignoredContactsByMatrixId[contact.mxGroupUser.userId] = contact;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
}
|
|
|
|
|
for (Contact *contact in invitedParticipants)
|
|
|
|
|
{
|
2019-01-07 23:24:11 +00:00
|
|
|
|
contactsDataSource.ignoredContactsByMatrixId[contact.mxGroupUser.userId] = contact;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[contactsPickerViewController showSearch:YES];
|
|
|
|
|
contactsPickerViewController.searchBar.placeholder = NSLocalizedStringFromTable(@"group_participants_invite_another_user", @"Vector", nil);
|
|
|
|
|
|
|
|
|
|
// Apply the search pattern if any
|
|
|
|
|
if (currentSearchText)
|
|
|
|
|
{
|
|
|
|
|
contactsPickerViewController.searchBar.text = currentSearchText;
|
|
|
|
|
[contactsDataSource searchWithPattern:currentSearchText forceReset:YES];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[contactsPickerViewController displayList:contactsDataSource];
|
|
|
|
|
|
|
|
|
|
[self pushViewController:contactsPickerViewController];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)refreshParticipantsList
|
|
|
|
|
{
|
|
|
|
|
if (_group)
|
|
|
|
|
{
|
|
|
|
|
actualParticipants = [[NSMutableArray alloc] initWithCapacity:_group.users.chunk.count];
|
|
|
|
|
for (MXGroupUser *groupUser in _group.users.chunk)
|
|
|
|
|
{
|
|
|
|
|
Contact *contact = [[Contact alloc] initMatrixContactWithDisplayName:groupUser.displayname andMatrixID:groupUser.userId];
|
|
|
|
|
contact.mxGroupUser = groupUser;
|
|
|
|
|
|
|
|
|
|
[actualParticipants addObject:contact];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
invitedParticipants = [[NSMutableArray alloc] initWithCapacity:_group.invitedUsers.chunk.count];
|
|
|
|
|
for (MXGroupUser *groupUser in _group.invitedUsers.chunk)
|
|
|
|
|
{
|
|
|
|
|
Contact *contact = [[Contact alloc] initMatrixContactWithDisplayName:groupUser.displayname andMatrixID:groupUser.userId];
|
|
|
|
|
contact.mxGroupUser = groupUser;
|
|
|
|
|
|
|
|
|
|
[invitedParticipants addObject:contact];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
actualParticipants = nil;
|
|
|
|
|
invitedParticipants = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self finalizeParticipantsList];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)finalizeParticipantsList
|
|
|
|
|
{
|
|
|
|
|
// Sort group participants by power and then alphabetically.
|
|
|
|
|
NSComparator comparator = ^NSComparisonResult(Contact *userA, Contact *userB) {
|
|
|
|
|
|
|
|
|
|
if (userA.mxGroupUser.isPrivileged && userB.mxGroupUser.isPrivileged)
|
|
|
|
|
{
|
|
|
|
|
return [userA.mxGroupUser.displayname compare:userB.mxGroupUser.displayname options:NSCaseInsensitiveSearch];
|
|
|
|
|
}
|
|
|
|
|
if (userA.mxGroupUser.isPrivileged)
|
|
|
|
|
{
|
|
|
|
|
return NSOrderedAscending;
|
|
|
|
|
}
|
|
|
|
|
if (userB.mxGroupUser.isPrivileged)
|
|
|
|
|
{
|
|
|
|
|
return NSOrderedDescending;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [userA.mxGroupUser.displayname compare:userB.mxGroupUser.displayname options:NSCaseInsensitiveSearch];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Sort each participants list in alphabetical order
|
|
|
|
|
[actualParticipants sortUsingComparator:comparator];
|
|
|
|
|
[invitedParticipants sortUsingComparator:comparator];
|
|
|
|
|
|
|
|
|
|
// Reload search result if any
|
|
|
|
|
if (currentSearchText.length)
|
|
|
|
|
{
|
|
|
|
|
NSString *searchText = currentSearchText;
|
|
|
|
|
currentSearchText = nil;
|
|
|
|
|
|
|
|
|
|
[self searchBar:_searchBarView textDidChange:searchText];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[self refreshTableView];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)addPendingActionMask
|
|
|
|
|
{
|
|
|
|
|
// Remove potential existing mask
|
|
|
|
|
[self removePendingActionMask];
|
|
|
|
|
|
|
|
|
|
// Add a spinner above the tableview to avoid that the user tap on any other button
|
|
|
|
|
pendingMaskSpinnerView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
|
|
|
|
|
pendingMaskSpinnerView.backgroundColor = [UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:0.5];
|
|
|
|
|
pendingMaskSpinnerView.frame = self.tableView.frame;
|
|
|
|
|
pendingMaskSpinnerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
|
|
|
|
|
|
|
|
|
|
// append it
|
|
|
|
|
[self.tableView.superview addSubview:pendingMaskSpinnerView];
|
|
|
|
|
|
|
|
|
|
// animate it
|
|
|
|
|
[pendingMaskSpinnerView startAnimating];
|
|
|
|
|
|
|
|
|
|
// Show the spinner after a delay so that if it is removed in a short future,
|
|
|
|
|
// it is not displayed to the end user.
|
|
|
|
|
pendingMaskSpinnerView.alpha = 0;
|
|
|
|
|
[UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
|
|
|
|
|
|
|
|
|
|
pendingMaskSpinnerView.alpha = 1;
|
|
|
|
|
|
|
|
|
|
} completion:^(BOOL finished) {
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)removePendingActionMask
|
|
|
|
|
{
|
|
|
|
|
if (pendingMaskSpinnerView)
|
|
|
|
|
{
|
|
|
|
|
[pendingMaskSpinnerView removeFromSuperview];
|
|
|
|
|
pendingMaskSpinnerView = nil;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)pushViewController:(UIViewController*)viewController
|
|
|
|
|
{
|
2018-01-16 22:49:04 +00:00
|
|
|
|
// Keep ref on pushed view controller
|
|
|
|
|
pushedViewController = viewController;
|
|
|
|
|
|
2018-01-08 18:17:29 +00:00
|
|
|
|
// Check whether the view controller is displayed inside a segmented one.
|
|
|
|
|
if (self.parentViewController.navigationController)
|
|
|
|
|
{
|
|
|
|
|
// Hide back button title
|
|
|
|
|
self.parentViewController.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
|
|
|
|
|
|
|
|
|
|
[self.parentViewController.navigationController pushViewController:viewController animated:YES];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Hide back button title
|
|
|
|
|
self.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
|
|
|
|
|
|
|
|
|
|
[self.navigationController pushViewController:viewController animated:YES];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-16 22:49:04 +00:00
|
|
|
|
- (void)releasePushedViewController
|
|
|
|
|
{
|
|
|
|
|
if (pushedViewController)
|
|
|
|
|
{
|
|
|
|
|
if ([pushedViewController isKindOfClass:[UINavigationController class]])
|
|
|
|
|
{
|
|
|
|
|
UINavigationController *navigationController = (UINavigationController*)pushedViewController;
|
|
|
|
|
for (id subViewController in navigationController.viewControllers)
|
|
|
|
|
{
|
|
|
|
|
if ([subViewController respondsToSelector:@selector(destroy)])
|
|
|
|
|
{
|
|
|
|
|
[subViewController destroy];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if ([pushedViewController respondsToSelector:@selector(destroy)])
|
|
|
|
|
{
|
|
|
|
|
[(id)pushedViewController destroy];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pushedViewController = nil;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-08 18:17:29 +00:00
|
|
|
|
#pragma mark - UITableView data source
|
|
|
|
|
|
|
|
|
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
|
|
|
|
{
|
|
|
|
|
NSInteger count = 0;
|
|
|
|
|
|
|
|
|
|
participantsSection = invitedSection = -1;
|
|
|
|
|
|
|
|
|
|
if (currentSearchText.length)
|
|
|
|
|
{
|
|
|
|
|
if (filteredActualParticipants.count)
|
|
|
|
|
{
|
|
|
|
|
participantsSection = count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (filteredInvitedParticipants.count)
|
|
|
|
|
{
|
|
|
|
|
invitedSection = count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (actualParticipants.count)
|
|
|
|
|
{
|
|
|
|
|
participantsSection = count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (invitedParticipants.count)
|
|
|
|
|
{
|
|
|
|
|
invitedSection = count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
|
|
|
|
{
|
|
|
|
|
NSInteger count = 0;
|
|
|
|
|
|
|
|
|
|
if (section == participantsSection)
|
|
|
|
|
{
|
|
|
|
|
if (currentSearchText.length)
|
|
|
|
|
{
|
|
|
|
|
count = filteredActualParticipants.count;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
count = actualParticipants.count;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (section == invitedSection)
|
|
|
|
|
{
|
|
|
|
|
if (currentSearchText.length)
|
|
|
|
|
{
|
|
|
|
|
count = filteredInvitedParticipants.count;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
count = invitedParticipants.count;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
|
|
|
{
|
|
|
|
|
UITableViewCell *cell;
|
|
|
|
|
|
|
|
|
|
if (indexPath.section == participantsSection || indexPath.section == invitedSection)
|
|
|
|
|
{
|
|
|
|
|
ContactTableViewCell* participantCell = [tableView dequeueReusableCellWithIdentifier:@"ParticipantTableViewCellId" forIndexPath:indexPath];
|
|
|
|
|
participantCell.selectionStyle = UITableViewCellSelectionStyleNone;
|
|
|
|
|
|
|
|
|
|
Contact *contact;
|
|
|
|
|
NSArray *participants;
|
|
|
|
|
|
|
|
|
|
if (indexPath.section == participantsSection)
|
|
|
|
|
{
|
|
|
|
|
if (currentSearchText.length)
|
|
|
|
|
{
|
|
|
|
|
participants = filteredActualParticipants;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
participants = actualParticipants;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (currentSearchText.length)
|
|
|
|
|
{
|
|
|
|
|
participants = filteredInvitedParticipants;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
participants = invitedParticipants;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (indexPath.row < participants.count)
|
|
|
|
|
{
|
|
|
|
|
contact = participants[indexPath.row];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (contact)
|
|
|
|
|
{
|
|
|
|
|
[participantCell render:contact];
|
|
|
|
|
|
|
|
|
|
// Update member badge
|
|
|
|
|
if (contact.mxGroupUser.isPrivileged)
|
|
|
|
|
{
|
|
|
|
|
participantCell.thumbnailBadgeView.image = [UIImage imageNamed:@"admin_icon"];
|
|
|
|
|
participantCell.thumbnailBadgeView.hidden = NO;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cell = participantCell;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Return a fake cell to prevent app from crashing.
|
|
|
|
|
cell = [[UITableViewCell alloc] init];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cell;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
|
|
|
|
|
{
|
|
|
|
|
if (indexPath.section == participantsSection || indexPath.section == invitedSection)
|
|
|
|
|
{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)tableView:(UITableView*)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath*)indexPath
|
|
|
|
|
{
|
|
|
|
|
// iOS8 requires this method to enable editing (see editActionsForRowAtIndexPath).
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - UITableView delegate
|
|
|
|
|
|
|
|
|
|
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
|
|
|
{
|
|
|
|
|
return tableView.estimatedRowHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section
|
|
|
|
|
{
|
|
|
|
|
if (section == invitedSection)
|
|
|
|
|
{
|
|
|
|
|
return tableView.estimatedSectionHeaderHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
|
|
|
|
|
{
|
2019-01-11 10:45:27 +00:00
|
|
|
|
cell.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
// Update the selected background view
|
2019-01-11 10:45:27 +00:00
|
|
|
|
if (ThemeService.shared.theme.selectedBackgroundColor)
|
2018-01-08 18:17:29 +00:00
|
|
|
|
{
|
|
|
|
|
cell.selectedBackgroundView = [[UIView alloc] init];
|
2019-01-11 10:45:27 +00:00
|
|
|
|
cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (tableView.style == UITableViewStylePlain)
|
|
|
|
|
{
|
|
|
|
|
cell.selectedBackgroundView = nil;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cell.selectedBackgroundView.backgroundColor = nil;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Refresh here the estimated row height
|
|
|
|
|
tableView.estimatedRowHeight = cell.frame.size.height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(nonnull UIView *)view forSection:(NSInteger)section
|
|
|
|
|
{
|
|
|
|
|
// Refresh here the estimated header height
|
|
|
|
|
tableView.estimatedSectionHeaderHeight = view.frame.size.height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
|
|
|
|
|
{
|
|
|
|
|
MXKTableViewHeaderFooterWithLabel *sectionHeader;
|
|
|
|
|
|
|
|
|
|
if (section == invitedSection)
|
|
|
|
|
{
|
|
|
|
|
sectionHeader = [tableView dequeueReusableHeaderFooterViewWithIdentifier:MXKTableViewHeaderFooterWithLabel.defaultReuseIdentifier];
|
2019-01-11 10:45:27 +00:00
|
|
|
|
sectionHeader.mxkContentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
|
|
|
|
|
sectionHeader.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
sectionHeader.mxkLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
|
|
|
|
|
|
|
|
|
|
sectionHeader.mxkLabel.text = NSLocalizedStringFromTable(@"group_participants_invited_section", @"Vector", nil);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sectionHeader;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
|
|
|
|
{
|
2018-01-16 22:49:04 +00:00
|
|
|
|
Contact *contact;
|
|
|
|
|
NSArray *participants;
|
|
|
|
|
|
|
|
|
|
if (indexPath.section == participantsSection)
|
|
|
|
|
{
|
|
|
|
|
if (currentSearchText.length)
|
|
|
|
|
{
|
|
|
|
|
participants = filteredActualParticipants;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
participants = actualParticipants;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (indexPath.section == invitedSection)
|
|
|
|
|
{
|
|
|
|
|
if (currentSearchText.length)
|
|
|
|
|
{
|
|
|
|
|
participants = filteredInvitedParticipants;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
participants = invitedParticipants;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (indexPath.row < participants.count)
|
|
|
|
|
{
|
|
|
|
|
contact = participants[indexPath.row];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (contact)
|
|
|
|
|
{
|
|
|
|
|
ContactDetailsViewController *contactDetailsViewController = [ContactDetailsViewController contactDetailsViewController];
|
|
|
|
|
contactDetailsViewController.enableVoipCall = NO;
|
|
|
|
|
contactDetailsViewController.contact = contact;
|
|
|
|
|
|
|
|
|
|
[self pushViewController:contactDetailsViewController];
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-08 18:17:29 +00:00
|
|
|
|
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
|
|
|
{
|
|
|
|
|
NSMutableArray* actions;
|
|
|
|
|
|
|
|
|
|
// add the swipe to delete only on participants sections
|
|
|
|
|
if (indexPath.section == participantsSection || indexPath.section == invitedSection)
|
|
|
|
|
{
|
|
|
|
|
actions = [[NSMutableArray alloc] init];
|
|
|
|
|
|
|
|
|
|
// Patch: Force the width of the button by adding whitespace characters into the title string.
|
|
|
|
|
UITableViewRowAction *leaveAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:@" " handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
|
|
|
|
|
|
|
|
|
|
[self onDeleteAt:indexPath];
|
|
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
|
2019-01-11 10:45:27 +00:00
|
|
|
|
leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon_blue" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(24, 24)];
|
2018-01-08 18:17:29 +00:00
|
|
|
|
[actions insertObject:leaveAction atIndex:0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return actions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - ContactsTableViewControllerDelegate
|
|
|
|
|
|
|
|
|
|
- (void)contactsTableViewController:(ContactsTableViewController *)contactsTableViewController didSelectContact:(MXKContact*)contact
|
|
|
|
|
{
|
|
|
|
|
[self didSelectInvitableContact:contact];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - Actions
|
|
|
|
|
|
|
|
|
|
- (void)onDeleteAt:(NSIndexPath*)path
|
|
|
|
|
{
|
|
|
|
|
if (path.section == participantsSection || path.section == invitedSection)
|
|
|
|
|
{
|
|
|
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
|
|
|
|
|
|
if (currentAlert)
|
|
|
|
|
{
|
|
|
|
|
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
|
|
|
|
currentAlert = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSMutableArray *participants;
|
|
|
|
|
Contact *contact;
|
|
|
|
|
|
|
|
|
|
if (path.section == participantsSection)
|
|
|
|
|
{
|
|
|
|
|
if (currentSearchText.length)
|
|
|
|
|
{
|
|
|
|
|
participants = filteredActualParticipants;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
participants = actualParticipants;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (currentSearchText.length)
|
|
|
|
|
{
|
|
|
|
|
participants = filteredInvitedParticipants;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
participants = invitedParticipants;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (path.row < participants.count)
|
|
|
|
|
{
|
|
|
|
|
contact = participants[path.row];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (contact && [contact.mxGroupUser.userId isEqualToString:self.mxSession.myUser.userId])
|
|
|
|
|
{
|
|
|
|
|
// Leave this group?
|
|
|
|
|
currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"group_participants_leave_prompt_title", @"Vector", nil)
|
|
|
|
|
message:NSLocalizedStringFromTable(@"group_participants_leave_prompt_msg", @"Vector", nil)
|
|
|
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
|
|
|
|
|
|
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
|
|
|
|
|
style:UIAlertActionStyleDefault
|
|
|
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
|
|
|
|
|
|
if (weakSelf)
|
|
|
|
|
{
|
|
|
|
|
typeof(self) self = weakSelf;
|
|
|
|
|
self->currentAlert = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}]];
|
|
|
|
|
|
|
|
|
|
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"leave", @"Vector", nil)
|
|
|
|
|
style:UIAlertActionStyleDefault
|
|
|
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
|
|
|
|
|
|
if (weakSelf)
|
|
|
|
|
{
|
|
|
|
|
typeof(self) self = weakSelf;
|
|
|
|
|
self->currentAlert = nil;
|
|
|
|
|
|
|
|
|
|
[self addPendingActionMask];
|
|
|
|
|
[self.mxSession leaveGroup:_group.groupId success:^{
|
|
|
|
|
|
|
|
|
|
[self withdrawViewControllerAnimated:YES completion:nil];
|
|
|
|
|
|
|
|
|
|
} failure:^(NSError *error) {
|
|
|
|
|
|
|
|
|
|
[self removePendingActionMask];
|
|
|
|
|
NSLog(@"[GroupParticipantsVC] Leave group %@ failed", _group.groupId);
|
|
|
|
|
// Alert user
|
|
|
|
|
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
|
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}]];
|
|
|
|
|
|
|
|
|
|
[currentAlert mxk_setAccessibilityIdentifier:@"GroupParticipantsVCLeaveAlert"];
|
|
|
|
|
[self presentViewController:currentAlert animated:YES completion:nil];
|
|
|
|
|
}
|
|
|
|
|
else if (contact)
|
|
|
|
|
{
|
|
|
|
|
NSString *memberUserId = contact.mxGroupUser.userId;
|
|
|
|
|
|
|
|
|
|
// Kick ?
|
|
|
|
|
NSString *promptMsg = [NSString stringWithFormat:NSLocalizedStringFromTable(@"group_participants_remove_prompt_msg", @"Vector", nil), (contact.mxGroupUser.displayname.length ? contact.mxGroupUser.displayname : memberUserId)];
|
|
|
|
|
currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"group_participants_remove_prompt_title", @"Vector", nil)
|
|
|
|
|
message:promptMsg
|
|
|
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
|
|
|
|
|
|
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
|
|
|
|
|
style:UIAlertActionStyleDefault
|
|
|
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
|
|
|
|
|
|
if (weakSelf)
|
|
|
|
|
{
|
|
|
|
|
typeof(self) self = weakSelf;
|
|
|
|
|
self->currentAlert = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}]];
|
|
|
|
|
|
|
|
|
|
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"remove", @"Vector", nil)
|
|
|
|
|
style:UIAlertActionStyleDefault
|
|
|
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
|
|
|
|
|
|
if (weakSelf)
|
|
|
|
|
{
|
|
|
|
|
typeof(self) self = weakSelf;
|
|
|
|
|
self->currentAlert = nil;
|
|
|
|
|
|
|
|
|
|
NSLog(@"[GroupParticipantsVC] Kick %@ failed", memberUserId);
|
|
|
|
|
// Alert user
|
|
|
|
|
[[AppDelegate theDelegate] showErrorAsAlert:[NSError errorWithDomain:@"GroupDomain" code:0 userInfo:@{NSLocalizedDescriptionKey:[NSBundle mxk_localizedStringForKey:@"not_supported_yet"]}]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}]];
|
|
|
|
|
|
|
|
|
|
[currentAlert mxk_setAccessibilityIdentifier:@"GroupParticipantsVCKickAlert"];
|
|
|
|
|
[self presentViewController:currentAlert animated:YES completion:nil];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
- (void)didSelectInvitableContact:(MXKContact*)contact
|
|
|
|
|
{
|
|
|
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
|
|
|
|
|
|
if (currentAlert)
|
|
|
|
|
{
|
|
|
|
|
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
|
|
|
|
currentAlert = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Invite ?
|
|
|
|
|
NSString *promptMsg = [NSString stringWithFormat:NSLocalizedStringFromTable(@"group_participants_invite_prompt_msg", @"Vector", nil), contact.displayName];
|
|
|
|
|
currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"group_participants_invite_prompt_title", @"Vector", nil)
|
|
|
|
|
message:promptMsg
|
|
|
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
|
|
|
|
|
|
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
|
|
|
|
|
style:UIAlertActionStyleDefault
|
|
|
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
|
|
|
|
|
|
if (weakSelf)
|
|
|
|
|
{
|
|
|
|
|
typeof(self) self = weakSelf;
|
|
|
|
|
self->currentAlert = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}]];
|
|
|
|
|
|
|
|
|
|
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"invite", @"Vector", nil)
|
|
|
|
|
style:UIAlertActionStyleDefault
|
|
|
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
|
|
|
|
|
|
if (weakSelf)
|
|
|
|
|
{
|
|
|
|
|
typeof(self) self = weakSelf;
|
|
|
|
|
self->currentAlert = nil;
|
|
|
|
|
|
|
|
|
|
NSArray *identifiers = contact.matrixIdentifiers;
|
|
|
|
|
NSString *participantId;
|
|
|
|
|
|
|
|
|
|
if (identifiers.count)
|
|
|
|
|
{
|
|
|
|
|
participantId = identifiers.firstObject;
|
|
|
|
|
|
|
|
|
|
NSLog(@"[GroupParticipantsVC] Invite %@ failed", participantId);
|
|
|
|
|
[[AppDelegate theDelegate] showErrorAsAlert:[NSError errorWithDomain:@"GroupDomain" code:0 userInfo:@{NSLocalizedDescriptionKey:[NSBundle mxk_localizedStringForKey:@"not_supported_yet"]}]];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}]];
|
|
|
|
|
|
|
|
|
|
[currentAlert mxk_setAccessibilityIdentifier:@"GroupParticipantsVCInviteAlert"];
|
|
|
|
|
[self presentViewController:currentAlert animated:YES completion:nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - UISearchBar delegate
|
|
|
|
|
|
|
|
|
|
- (void)refreshSearchBarItemsColor:(UISearchBar *)searchBar
|
|
|
|
|
{
|
|
|
|
|
// bar tint color
|
2019-01-11 10:57:02 +00:00
|
|
|
|
searchBar.barTintColor = searchBar.tintColor = ThemeService.shared.riotColorBlue;
|
|
|
|
|
searchBar.tintColor = ThemeService.shared.riotColorBlue;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
// FIXME: this all seems incredibly fragile and tied to gutwrenching the current UISearchBar internals.
|
|
|
|
|
|
|
|
|
|
// text color
|
|
|
|
|
UITextField *searchBarTextField = [searchBar valueForKey:@"_searchField"];
|
2019-01-11 10:45:27 +00:00
|
|
|
|
searchBarTextField.textColor = ThemeService.shared.theme.textSecondaryColor;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
// Magnifying glass icon.
|
|
|
|
|
UIImageView *leftImageView = (UIImageView *)searchBarTextField.leftView;
|
|
|
|
|
leftImageView.image = [leftImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
2019-01-11 10:57:02 +00:00
|
|
|
|
leftImageView.tintColor = ThemeService.shared.riotColorBlue;
|
2018-01-08 18:17:29 +00:00
|
|
|
|
|
|
|
|
|
// remove the gray background color
|
|
|
|
|
UIView *effectBackgroundTop = [searchBarTextField valueForKey:@"_effectBackgroundTop"];
|
|
|
|
|
UIView *effectBackgroundBottom = [searchBarTextField valueForKey:@"_effectBackgroundBottom"];
|
|
|
|
|
effectBackgroundTop.hidden = YES;
|
|
|
|
|
effectBackgroundBottom.hidden = YES;
|
|
|
|
|
|
|
|
|
|
// place holder
|
|
|
|
|
searchBarTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:searchBarTextField.placeholder
|
|
|
|
|
attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle),
|
2019-01-11 10:57:02 +00:00
|
|
|
|
NSUnderlineColorAttributeName: ThemeService.shared.riotColorBlue,
|
|
|
|
|
NSForegroundColorAttributeName: ThemeService.shared.riotColorBlue}];
|
2018-01-08 18:17:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
|
|
|
|
|
{
|
|
|
|
|
// Update search results.
|
|
|
|
|
NSUInteger index;
|
|
|
|
|
MXKContact *contact;
|
|
|
|
|
|
|
|
|
|
searchText = [searchText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
|
|
|
|
|
|
|
|
|
if (!currentSearchText.length || [searchText hasPrefix:currentSearchText] == NO)
|
|
|
|
|
{
|
|
|
|
|
// Copy participants and invited participants
|
|
|
|
|
filteredActualParticipants = [NSMutableArray arrayWithArray:actualParticipants];
|
|
|
|
|
filteredInvitedParticipants = [NSMutableArray arrayWithArray:invitedParticipants];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentSearchText = searchText;
|
|
|
|
|
|
|
|
|
|
// Filter group participants
|
|
|
|
|
if (currentSearchText.length)
|
|
|
|
|
{
|
|
|
|
|
for (index = 0; index < filteredActualParticipants.count;)
|
|
|
|
|
{
|
|
|
|
|
contact = filteredActualParticipants[index];
|
|
|
|
|
if (![contact matchedWithPatterns:@[currentSearchText]])
|
|
|
|
|
{
|
|
|
|
|
[filteredActualParticipants removeObjectAtIndex:index];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (index = 0; index < filteredInvitedParticipants.count;)
|
|
|
|
|
{
|
|
|
|
|
contact = filteredInvitedParticipants[index];
|
|
|
|
|
if (![contact matchedWithPatterns:@[currentSearchText]])
|
|
|
|
|
{
|
|
|
|
|
[filteredInvitedParticipants removeObjectAtIndex:index];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
filteredActualParticipants = nil;
|
|
|
|
|
filteredInvitedParticipants = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Refresh display
|
|
|
|
|
[self refreshTableView];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
|
|
|
|
|
{
|
|
|
|
|
searchBar.showsCancelButton = YES;
|
|
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
|
|
|
|
|
{
|
|
|
|
|
searchBar.showsCancelButton = NO;
|
|
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
|
|
|
|
|
{
|
|
|
|
|
// "Done" key has been pressed.
|
|
|
|
|
|
|
|
|
|
// Dismiss keyboard
|
|
|
|
|
[_searchBarView resignFirstResponder];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
|
|
|
|
|
{
|
|
|
|
|
if (currentSearchText)
|
|
|
|
|
{
|
|
|
|
|
currentSearchText = nil;
|
|
|
|
|
filteredActualParticipants = nil;
|
|
|
|
|
filteredInvitedParticipants = nil;
|
|
|
|
|
|
|
|
|
|
[self refreshTableView];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
searchBar.text = nil;
|
|
|
|
|
// Leave search
|
|
|
|
|
[searchBar resignFirstResponder];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|