mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-18 23:48:29 +02:00
Merge MatrixKit develop with commit hash: b85b736313bec0592bd1cabc68035d97f5331137
This commit is contained in:
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 New Vector 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 "MXKRoomMemberListViewController.h"
|
||||
|
||||
#import "MXKRoomMemberTableViewCell.h"
|
||||
|
||||
#import "MXKConstants.h"
|
||||
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
#import "MXKSwiftHeader.h"
|
||||
|
||||
@interface MXKRoomMemberListViewController ()
|
||||
{
|
||||
/**
|
||||
The data source providing UITableViewCells
|
||||
*/
|
||||
MXKRoomMemberListDataSource *dataSource;
|
||||
|
||||
/**
|
||||
Timer used to update members presence
|
||||
*/
|
||||
NSTimer* presenceUpdateTimer;
|
||||
|
||||
/**
|
||||
Optional bar buttons
|
||||
*/
|
||||
UIBarButtonItem *searchBarButton;
|
||||
UIBarButtonItem *addBarButton;
|
||||
|
||||
/**
|
||||
The current displayed alert (if any).
|
||||
*/
|
||||
UIAlertController *currentAlert;
|
||||
|
||||
/**
|
||||
Search bar
|
||||
*/
|
||||
BOOL ignoreSearchRequest;
|
||||
|
||||
/**
|
||||
Observe kMXSessionWillLeaveRoomNotification to be notified if the user leaves the current room.
|
||||
*/
|
||||
id leaveRoomNotificationObserver;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MXKRoomMemberListViewController
|
||||
@synthesize dataSource;
|
||||
|
||||
#pragma mark - Class methods
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([MXKRoomMemberListViewController class])
|
||||
bundle:[NSBundle bundleForClass:[MXKRoomMemberListViewController class]]];
|
||||
}
|
||||
|
||||
+ (instancetype)roomMemberListViewController
|
||||
{
|
||||
return [[[self class] alloc] initWithNibName:NSStringFromClass([MXKRoomMemberListViewController class])
|
||||
bundle:[NSBundle bundleForClass:[MXKRoomMemberListViewController class]]];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)finalizeInit
|
||||
{
|
||||
[super finalizeInit];
|
||||
|
||||
// Enable both bar button by default.
|
||||
_enableMemberInvitation = YES;
|
||||
_enableMemberSearch = YES;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
// Check whether the view controller has been pushed via storyboard
|
||||
if (!self.membersTableView)
|
||||
{
|
||||
// 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:@[_membersSearchBarTopConstraint, _membersTableViewBottomConstraint]];
|
||||
|
||||
_membersSearchBarTopConstraint = [NSLayoutConstraint constraintWithItem:self.topLayoutGuide
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.membersSearchBar
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1.0f
|
||||
constant:0.0f];
|
||||
|
||||
_membersTableViewBottomConstraint = [NSLayoutConstraint constraintWithItem:self.bottomLayoutGuide
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.membersTableView
|
||||
attribute:NSLayoutAttributeBottom
|
||||
multiplier:1.0f
|
||||
constant:0.0f];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[_membersSearchBarTopConstraint, _membersTableViewBottomConstraint]];
|
||||
|
||||
// Hide search bar by default
|
||||
self.membersSearchBar.hidden = YES;
|
||||
self.membersSearchBarHeightConstraint.constant = 0;
|
||||
[self.view setNeedsUpdateConstraints];
|
||||
|
||||
searchBarButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(search:)];
|
||||
addBarButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(inviteNewMember:)];
|
||||
|
||||
// Refresh bar button display.
|
||||
[self refreshUIBarButtons];
|
||||
|
||||
// Add an accessory view to the search bar in order to retrieve keyboard view.
|
||||
self.membersSearchBar.inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
|
||||
// Finalize table view configuration
|
||||
self.membersTableView.delegate = self;
|
||||
self.membersTableView.dataSource = dataSource; // Note datasource may be nil here.
|
||||
|
||||
// Set up default table view cell class
|
||||
[self.membersTableView registerNib:MXKRoomMemberTableViewCell.nib forCellReuseIdentifier:MXKRoomMemberTableViewCell.defaultReuseIdentifier];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
// Check whether the user still belongs to the room's members.
|
||||
if (self.dataSource && [self.mainSession roomWithRoomId:self.dataSource.roomId])
|
||||
{
|
||||
[self refreshUIBarButtons];
|
||||
|
||||
// Observe kMXSessionWillLeaveRoomNotification to be notified if the user leaves the current room.
|
||||
leaveRoomNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionWillLeaveRoomNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif)
|
||||
{
|
||||
|
||||
// Check whether the user will leave the room related to the displayed member list
|
||||
if (notif.object == self.mainSession)
|
||||
{
|
||||
NSString *roomId = notif.userInfo[kMXSessionNotificationRoomIdKey];
|
||||
if (roomId && [roomId isEqualToString:self.dataSource.roomId])
|
||||
{
|
||||
// We remove the current view controller.
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
// We remove the current view controller.
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Restore search mechanism (if enabled)
|
||||
ignoreSearchRequest = NO;
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
// The user may still press search button whereas the view disappears
|
||||
ignoreSearchRequest = YES;
|
||||
|
||||
if (leaveRoomNotificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:leaveRoomNotificationObserver];
|
||||
leaveRoomNotificationObserver = nil;
|
||||
}
|
||||
|
||||
// Leave potential search session
|
||||
if (!self.membersSearchBar.isHidden)
|
||||
{
|
||||
[self searchBarCancelButtonClicked:self.membersSearchBar];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
self.membersSearchBar.inputAccessoryView = nil;
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
{
|
||||
[super didReceiveMemoryWarning];
|
||||
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
#pragma mark - Override MXKTableViewController
|
||||
|
||||
- (void)onKeyboardShowAnimationComplete
|
||||
{
|
||||
// Report the keyboard view in order to track keyboard frame changes
|
||||
self.keyboardView = _membersSearchBar.inputAccessoryView.superview;
|
||||
}
|
||||
|
||||
- (void)setKeyboardHeight:(CGFloat)keyboardHeight
|
||||
{
|
||||
// Deduce the bottom constraint for the table view (Don't forget the potential tabBar)
|
||||
CGFloat tableViewBottomConst = keyboardHeight - self.bottomLayoutGuide.length;
|
||||
// Check whether the keyboard is over the tabBar
|
||||
if (tableViewBottomConst < 0)
|
||||
{
|
||||
tableViewBottomConst = 0;
|
||||
}
|
||||
|
||||
// Update constraints
|
||||
_membersTableViewBottomConstraint.constant = tableViewBottomConst;
|
||||
|
||||
// Force layout immediately to take into account new constraint
|
||||
[self.view layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
if (presenceUpdateTimer)
|
||||
{
|
||||
[presenceUpdateTimer invalidate];
|
||||
presenceUpdateTimer = nil;
|
||||
}
|
||||
|
||||
self.membersTableView.dataSource = nil;
|
||||
self.membersTableView.delegate = nil;
|
||||
self.membersTableView = nil;
|
||||
dataSource.delegate = nil;
|
||||
dataSource = nil;
|
||||
|
||||
if (currentAlert)
|
||||
{
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
currentAlert = nil;
|
||||
}
|
||||
|
||||
searchBarButton = nil;
|
||||
addBarButton = nil;
|
||||
|
||||
_delegate = nil;
|
||||
|
||||
[super destroy];
|
||||
}
|
||||
|
||||
#pragma mark - Internal methods
|
||||
|
||||
- (void)updateMembersActivityInfo
|
||||
{
|
||||
for (id memberCell in self.membersTableView.visibleCells)
|
||||
{
|
||||
if ([memberCell respondsToSelector:@selector(updateActivityInfo)])
|
||||
{
|
||||
[memberCell updateActivityInfo];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UIBarButton handling
|
||||
|
||||
- (void)setEnableMemberSearch:(BOOL)enableMemberSearch
|
||||
{
|
||||
_enableMemberSearch = enableMemberSearch;
|
||||
[self refreshUIBarButtons];
|
||||
}
|
||||
|
||||
- (void)setEnableMemberInvitation:(BOOL)enableMemberInvitation
|
||||
{
|
||||
_enableMemberInvitation = enableMemberInvitation;
|
||||
[self refreshUIBarButtons];
|
||||
}
|
||||
|
||||
- (void)refreshUIBarButtons
|
||||
{
|
||||
MXRoom *mxRoom = [self.mainSession roomWithRoomId:dataSource.roomId];
|
||||
|
||||
MXWeakify(self);
|
||||
[mxRoom state:^(MXRoomState *roomState) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
BOOL showInvitationOption = self.enableMemberInvitation;
|
||||
|
||||
if (showInvitationOption && self->dataSource)
|
||||
{
|
||||
// Check conditions to be able to invite someone
|
||||
NSInteger oneSelfPowerLevel = [roomState.powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
|
||||
if (oneSelfPowerLevel < [roomState.powerLevels invite])
|
||||
{
|
||||
showInvitationOption = NO;
|
||||
}
|
||||
}
|
||||
|
||||
if (showInvitationOption)
|
||||
{
|
||||
if (self.enableMemberSearch)
|
||||
{
|
||||
self.navigationItem.rightBarButtonItems = @[self->searchBarButton, self->addBarButton];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.navigationItem.rightBarButtonItems = @[self->addBarButton];
|
||||
}
|
||||
}
|
||||
else if (self.enableMemberSearch)
|
||||
{
|
||||
self.navigationItem.rightBarButtonItems = @[self->searchBarButton];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.navigationItem.rightBarButtonItems = nil;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
- (void)displayList:(MXKRoomMemberListDataSource *)listDataSource
|
||||
{
|
||||
if (dataSource)
|
||||
{
|
||||
dataSource.delegate = nil;
|
||||
dataSource = nil;
|
||||
[self removeMatrixSession:self.mainSession];
|
||||
}
|
||||
|
||||
dataSource = listDataSource;
|
||||
dataSource.delegate = self;
|
||||
|
||||
// Report the matrix session at view controller level to update UI according to session state
|
||||
[self addMatrixSession:dataSource.mxSession];
|
||||
|
||||
if (self.membersTableView)
|
||||
{
|
||||
// Set up table data source
|
||||
self.membersTableView.dataSource = dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollToTop:(BOOL)animated
|
||||
{
|
||||
[self.membersTableView setContentOffset:CGPointMake(-self.membersTableView.adjustedContentInset.left, -self.membersTableView.adjustedContentInset.top) animated:animated];
|
||||
}
|
||||
|
||||
#pragma mark - MXKDataSourceDelegate
|
||||
|
||||
- (Class<MXKCellRendering>)cellViewClassForCellData:(MXKCellData*)cellData
|
||||
{
|
||||
// Return the default member table view cell
|
||||
return MXKRoomMemberTableViewCell.class;
|
||||
}
|
||||
|
||||
- (NSString *)cellReuseIdentifierForCellData:(MXKCellData*)cellData
|
||||
{
|
||||
// Consider the default member table view cell
|
||||
return MXKRoomMemberTableViewCell.defaultReuseIdentifier;
|
||||
}
|
||||
|
||||
- (void)dataSource:(MXKDataSource *)dataSource didCellChange:(id)changes
|
||||
{
|
||||
if (presenceUpdateTimer)
|
||||
{
|
||||
[presenceUpdateTimer invalidate];
|
||||
presenceUpdateTimer = nil;
|
||||
}
|
||||
|
||||
// For now, do a simple full reload
|
||||
[self.membersTableView reloadData];
|
||||
|
||||
if (shouldScrollToTopOnRefresh)
|
||||
{
|
||||
[self scrollToTop:NO];
|
||||
shouldScrollToTopOnRefresh = NO;
|
||||
}
|
||||
|
||||
// Place a timer to update members's activity information
|
||||
presenceUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(updateMembersActivityInfo) userInfo:self repeats:YES];
|
||||
}
|
||||
|
||||
#pragma mark - UITableView delegate
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return [dataSource cellHeightAtIndex:indexPath.row];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (_delegate)
|
||||
{
|
||||
id<MXKRoomMemberCellDataStoring> cellData = [dataSource cellDataAtIndex:indexPath.row];
|
||||
|
||||
[_delegate roomMemberListViewController:self didSelectMember:cellData.roomMember];
|
||||
}
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:NO];
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
|
||||
{
|
||||
// Release here resources, and restore reusable cells
|
||||
if ([cell respondsToSelector:@selector(didEndDisplay)])
|
||||
{
|
||||
[(id<MXKCellRendering>)cell didEndDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UISearchBarDelegate
|
||||
|
||||
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
|
||||
{
|
||||
// Apply filter
|
||||
shouldScrollToTopOnRefresh = YES;
|
||||
if (searchText.length)
|
||||
{
|
||||
[self.dataSource searchWithPatterns:@[searchText]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self.dataSource searchWithPatterns:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
|
||||
{
|
||||
// "Done" key has been pressed
|
||||
[searchBar resignFirstResponder];
|
||||
}
|
||||
|
||||
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
|
||||
{
|
||||
// Leave search
|
||||
[searchBar resignFirstResponder];
|
||||
|
||||
self.membersSearchBar.hidden = YES;
|
||||
self.membersSearchBarHeightConstraint.constant = 0;
|
||||
[self.view setNeedsUpdateConstraints];
|
||||
|
||||
self.membersSearchBar.text = nil;
|
||||
|
||||
// Refresh display
|
||||
shouldScrollToTopOnRefresh = YES;
|
||||
[self.dataSource searchWithPatterns:nil];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)search:(id)sender
|
||||
{
|
||||
// The user may have pressed search button whereas the view controller was disappearing
|
||||
if (ignoreSearchRequest)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.membersSearchBar.isHidden)
|
||||
{
|
||||
// Check whether there are data in which search
|
||||
if ([self.dataSource tableView:self.membersTableView numberOfRowsInSection:0])
|
||||
{
|
||||
self.membersSearchBar.hidden = NO;
|
||||
self.membersSearchBarHeightConstraint.constant = 44;
|
||||
[self.view setNeedsUpdateConstraints];
|
||||
|
||||
// Create search bar
|
||||
[self.membersSearchBar becomeFirstResponder];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self searchBarCancelButtonClicked: self.membersSearchBar];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)inviteNewMember:(id)sender
|
||||
{
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
if (currentAlert)
|
||||
{
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
}
|
||||
|
||||
// Ask for userId to invite
|
||||
currentAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n userIdTitle] message:nil preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->currentAlert = nil;
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
|
||||
[currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField)
|
||||
{
|
||||
textField.secureTextEntry = NO;
|
||||
textField.placeholder = [MatrixKitL10n userIdPlaceholder];
|
||||
}];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n invite]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
NSString *userId = [self->currentAlert textFields].firstObject.text;
|
||||
|
||||
self->currentAlert = nil;
|
||||
|
||||
if (userId.length)
|
||||
{
|
||||
MXRoom *mxRoom = [self.mainSession roomWithRoomId:self.dataSource.roomId];
|
||||
if (mxRoom)
|
||||
{
|
||||
[mxRoom inviteUser:userId success:^{
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
MXLogDebug(@"[MXKRoomVC] Invite %@ failed", userId);
|
||||
// Notify MatrixKit user
|
||||
NSString *myUserId = self.mainSession.myUser.userId;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
|
||||
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[self presentViewController:currentAlert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user