2014-10-02 15:02:47 +00:00
|
|
|
/*
|
|
|
|
Copyright 2014 OpenMarket 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.
|
|
|
|
*/
|
2014-11-17 16:32:46 +00:00
|
|
|
|
2014-10-03 17:26:39 +00:00
|
|
|
#import "RoomViewController.h"
|
2014-12-23 17:51:49 +00:00
|
|
|
#import "MemberViewController.h"
|
2015-03-31 13:09:17 +00:00
|
|
|
|
|
|
|
#import "MXKRoomBubbleTableViewCell.h"
|
|
|
|
|
2014-10-18 01:03:55 +00:00
|
|
|
#import "RoomMemberTableCell.h"
|
2014-12-15 15:54:31 +00:00
|
|
|
#import "RoomTitleView.h"
|
2014-10-02 15:02:47 +00:00
|
|
|
|
2015-01-23 14:36:05 +00:00
|
|
|
#import "MatrixSDKHandler.h"
|
2014-10-14 15:56:03 +00:00
|
|
|
#import "AppDelegate.h"
|
2014-11-05 11:57:24 +00:00
|
|
|
#import "AppSettings.h"
|
2014-10-14 15:56:03 +00:00
|
|
|
|
2014-11-07 21:58:26 +00:00
|
|
|
#import "MediaManager.h"
|
2015-01-23 12:46:27 +00:00
|
|
|
#import "MXCTools.h"
|
2014-11-07 21:58:26 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
// TODO GFO
|
2014-12-04 15:33:31 +00:00
|
|
|
#define ROOMVIEWCONTROLLER_UPLOAD_FILE_SIZE 5000000
|
2015-01-15 12:17:00 +00:00
|
|
|
|
2014-10-09 17:06:30 +00:00
|
|
|
|
2014-11-10 18:01:49 +00:00
|
|
|
@interface RoomViewController () {
|
2014-11-14 14:29:28 +00:00
|
|
|
BOOL forceScrollToBottomOnViewDidAppear;
|
2014-10-27 17:40:04 +00:00
|
|
|
BOOL isJoinRequestInProgress;
|
2014-11-21 09:54:49 +00:00
|
|
|
|
2014-10-17 13:54:59 +00:00
|
|
|
// Members list
|
2014-11-04 16:26:33 +00:00
|
|
|
NSArray *members;
|
2014-10-28 16:27:15 +00:00
|
|
|
id membersListener;
|
2014-11-07 21:58:26 +00:00
|
|
|
|
2015-01-28 10:30:15 +00:00
|
|
|
// the user taps on a member thumbnail
|
|
|
|
MXRoomMember *selectedRoomMember;
|
2014-10-14 15:56:03 +00:00
|
|
|
}
|
2014-10-02 15:02:47 +00:00
|
|
|
|
2014-10-15 14:13:35 +00:00
|
|
|
@property (weak, nonatomic) IBOutlet UINavigationItem *roomNavItem;
|
2015-02-23 14:46:57 +00:00
|
|
|
@property (strong, nonatomic) IBOutlet UIBarButtonItem *showRoomMembersButtonItem;
|
2014-12-15 15:54:31 +00:00
|
|
|
@property (weak, nonatomic) IBOutlet RoomTitleView *roomTitleView;
|
2015-02-23 14:46:57 +00:00
|
|
|
|
2014-10-28 14:45:10 +00:00
|
|
|
@property (weak, nonatomic) IBOutlet UIView *membersView;
|
|
|
|
@property (weak, nonatomic) IBOutlet UITableView *membersTableView;
|
2014-10-13 16:53:33 +00:00
|
|
|
|
2015-01-23 12:46:27 +00:00
|
|
|
@property (strong, nonatomic) MXCAlert *actionMenu;
|
2015-01-13 16:53:31 +00:00
|
|
|
|
2014-10-02 15:02:47 +00:00
|
|
|
@end
|
|
|
|
|
2014-10-03 17:26:39 +00:00
|
|
|
@implementation RoomViewController
|
2014-10-02 15:02:47 +00:00
|
|
|
|
|
|
|
- (void)viewDidLoad {
|
|
|
|
[super viewDidLoad];
|
2015-02-23 14:46:57 +00:00
|
|
|
|
2014-10-02 15:02:47 +00:00
|
|
|
// Do any additional setup after loading the view, typically from a nib.
|
2014-11-14 14:29:28 +00:00
|
|
|
forceScrollToBottomOnViewDidAppear = YES;
|
2015-03-31 13:09:17 +00:00
|
|
|
// TODO GFO Hide messages table by default in order to hide initial scrolling to the bottom
|
|
|
|
// self.tableView.hidden = YES;
|
2014-12-18 13:36:55 +00:00
|
|
|
|
|
|
|
// ensure that the titleView will be scaled when it will be required
|
|
|
|
// during a screen rotation for example.
|
|
|
|
self.roomTitleView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
2014-10-02 15:02:47 +00:00
|
|
|
}
|
|
|
|
|
2014-10-09 17:06:30 +00:00
|
|
|
- (void)dealloc {
|
2014-10-17 13:54:59 +00:00
|
|
|
members = nil;
|
2014-10-28 16:27:15 +00:00
|
|
|
if (membersListener) {
|
|
|
|
membersListener = nil;
|
|
|
|
}
|
2014-11-04 16:26:33 +00:00
|
|
|
|
2014-11-10 18:01:49 +00:00
|
|
|
if (self.actionMenu) {
|
|
|
|
[self.actionMenu dismiss:NO];
|
|
|
|
self.actionMenu = nil;
|
2014-11-04 16:26:33 +00:00
|
|
|
}
|
2014-10-09 17:06:30 +00:00
|
|
|
}
|
|
|
|
|
2014-10-02 15:02:47 +00:00
|
|
|
- (void)didReceiveMemoryWarning {
|
|
|
|
[super didReceiveMemoryWarning];
|
|
|
|
// Dispose of any resources that can be recreated.
|
|
|
|
}
|
|
|
|
|
2014-10-13 16:53:33 +00:00
|
|
|
- (void)viewWillAppear:(BOOL)animated {
|
|
|
|
[super viewWillAppear:animated];
|
|
|
|
|
2014-12-24 15:32:24 +00:00
|
|
|
// Register a listener for events that concern room members
|
2015-01-23 14:36:05 +00:00
|
|
|
MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler];
|
2014-12-24 15:32:24 +00:00
|
|
|
NSArray *mxMembersEvents = @[
|
|
|
|
kMXEventTypeStringRoomMember,
|
|
|
|
kMXEventTypeStringRoomPowerLevels,
|
|
|
|
kMXEventTypeStringPresence
|
|
|
|
];
|
|
|
|
membersListener = [mxHandler.mxSession listenToEventsOfTypes:mxMembersEvents onEvent:^(MXEvent *event, MXEventDirection direction, id customObject) {
|
|
|
|
// consider only live event
|
|
|
|
if (direction == MXEventDirectionForwards) {
|
|
|
|
// Check the room Id (if any)
|
2015-03-31 13:09:17 +00:00
|
|
|
if (event.roomId && [event.roomId isEqualToString:self.dataSource.roomId] == NO) {
|
2014-12-24 15:32:24 +00:00
|
|
|
// This event does not concern the current room members
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-28 17:39:23 +00:00
|
|
|
// Check whether no text field is editing before refreshing title view
|
|
|
|
if (!self.roomTitleView.isEditing) {
|
|
|
|
[self.roomTitleView refreshDisplay];
|
|
|
|
}
|
2014-12-24 15:32:24 +00:00
|
|
|
|
|
|
|
// refresh the
|
|
|
|
if (members.count > 0) {
|
|
|
|
// Hide potential action sheet
|
|
|
|
if (self.actionMenu) {
|
|
|
|
[self.actionMenu dismiss:NO];
|
|
|
|
self.actionMenu = nil;
|
|
|
|
}
|
|
|
|
// Refresh members list
|
|
|
|
[self updateRoomMembers];
|
|
|
|
[self.membersTableView reloadData];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}];
|
2014-10-13 16:53:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)viewWillDisappear:(BOOL)animated {
|
|
|
|
[super viewWillDisappear:animated];
|
|
|
|
|
2015-02-16 09:37:17 +00:00
|
|
|
MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler];
|
|
|
|
|
2014-11-04 16:26:33 +00:00
|
|
|
// hide action
|
2014-11-10 18:01:49 +00:00
|
|
|
if (self.actionMenu) {
|
|
|
|
[self.actionMenu dismiss:NO];
|
|
|
|
self.actionMenu = nil;
|
2014-11-04 16:26:33 +00:00
|
|
|
}
|
|
|
|
|
2014-10-17 13:54:59 +00:00
|
|
|
// Hide members by default
|
2015-02-23 14:46:57 +00:00
|
|
|
[self hideRoomMembers:nil];
|
2014-10-17 13:54:59 +00:00
|
|
|
|
2015-02-16 09:37:17 +00:00
|
|
|
// Store the potential message partially typed in text input
|
2015-03-31 13:09:17 +00:00
|
|
|
[mxHandler storePartialTextMessage:self.inputToolbarView.textMessage forRoomId:self.dataSource.roomId];
|
2014-12-22 10:52:29 +00:00
|
|
|
|
2014-12-24 15:32:24 +00:00
|
|
|
if (membersListener) {
|
|
|
|
[mxHandler.mxSession removeListener:membersListener];
|
|
|
|
membersListener = nil;
|
|
|
|
}
|
2014-10-13 16:53:33 +00:00
|
|
|
}
|
|
|
|
|
2014-10-14 15:56:03 +00:00
|
|
|
- (void)viewDidAppear:(BOOL)animated {
|
|
|
|
[super viewDidAppear:animated];
|
|
|
|
|
2014-12-09 09:39:56 +00:00
|
|
|
// Set visible room id
|
2015-03-31 13:09:17 +00:00
|
|
|
[AppDelegate theDelegate].masterTabBarController.visibleRoomId = self.dataSource.roomId;
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
// if (forceScrollToBottomOnViewDidAppear) {
|
|
|
|
// dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
// if (nil == _mxRoom) {
|
|
|
|
// // The view controller has been released. Not need to go further
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
// // Scroll to the bottom
|
|
|
|
// [self scrollMessagesTableViewToBottomAnimated:animated];
|
|
|
|
// });
|
|
|
|
// forceScrollToBottomOnViewDidAppear = NO;
|
|
|
|
//// self.tableView.hidden = NO;
|
|
|
|
// }
|
2014-12-24 10:10:41 +00:00
|
|
|
|
2015-01-22 10:09:14 +00:00
|
|
|
[self updateUI];
|
2015-02-16 15:20:33 +00:00
|
|
|
|
|
|
|
// Retrieve the potential message partially typed during last room display.
|
|
|
|
// Note: We have to wait for viewDidAppear before updating growingTextView (viewWillAppear is too early)
|
2015-03-31 13:09:17 +00:00
|
|
|
self.inputToolbarView.textMessage = [[MatrixSDKHandler sharedHandler] partialTextMessageForRoomId:self.dataSource.roomId];
|
2014-10-14 15:56:03 +00:00
|
|
|
}
|
|
|
|
|
2014-12-09 09:39:56 +00:00
|
|
|
- (void)viewDidDisappear:(BOOL)animated {
|
|
|
|
[super viewDidDisappear:animated];
|
|
|
|
|
|
|
|
// Reset visible room id
|
|
|
|
[AppDelegate theDelegate].masterTabBarController.visibleRoomId = nil;
|
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
#pragma mark -
|
2015-01-20 11:41:27 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
- (void)dismissKeyboard {
|
2015-01-16 09:50:15 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
[_roomTitleView dismissKeyboard];
|
|
|
|
[super dismissKeyboard];
|
2014-12-22 10:52:29 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
- (void)didMatrixSessionStateChange {
|
|
|
|
|
|
|
|
[super didMatrixSessionStateChange];
|
2015-03-31 15:51:25 +00:00
|
|
|
|
|
|
|
// Check dataSource state
|
|
|
|
if (self.dataSource.state == MXKDataSourceStatePreparing) {
|
2015-03-31 13:09:17 +00:00
|
|
|
// dataSource is not ready, keep running the loading wheel
|
|
|
|
[self.activityIndicator startAnimating];
|
2015-02-25 16:58:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-22 10:09:14 +00:00
|
|
|
- (void)updateUI {
|
2015-03-31 13:09:17 +00:00
|
|
|
|
2015-03-31 15:51:25 +00:00
|
|
|
// Update UI by considering dataSource state
|
|
|
|
if (self.dataSource && self.dataSource.state == MXKDataSourceStateReady) {
|
2015-03-31 13:09:17 +00:00
|
|
|
// Here the activityIndicator should be stopped, we call `didMatrixSessionStateChange` to take
|
|
|
|
// into account mxSession state before stopping activity indicator.
|
|
|
|
[super didMatrixSessionStateChange];
|
|
|
|
|
|
|
|
// Show input tool bar
|
|
|
|
self.inputToolbarView.hidden = NO;
|
|
|
|
|
2015-01-22 10:09:14 +00:00
|
|
|
// Check room members to enable/disable members button in nav bar
|
2015-03-31 13:09:17 +00:00
|
|
|
self.showRoomMembersButtonItem.enabled = ([self.dataSource.room.state members].count != 0);
|
|
|
|
|
|
|
|
self.roomTitleView.mxRoom = self.dataSource.room;
|
|
|
|
self.roomTitleView.editable = YES;
|
2015-03-31 15:51:25 +00:00
|
|
|
self.roomTitleView.hidden = NO;
|
2015-03-31 13:09:17 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
self.inputToolbarView.hidden = YES;
|
2015-02-23 14:46:57 +00:00
|
|
|
self.showRoomMembersButtonItem.enabled = NO;
|
2015-03-31 13:09:17 +00:00
|
|
|
|
2015-03-31 15:51:25 +00:00
|
|
|
if (self.dataSource && self.dataSource.state == MXKDataSourceStatePreparing) {
|
|
|
|
self.roomTitleView.mxRoom = self.dataSource.room;
|
|
|
|
self.roomTitleView.hidden = (!self.roomTitleView.mxRoom);
|
|
|
|
} else {
|
|
|
|
self.roomTitleView.mxRoom = nil;
|
|
|
|
self.roomTitleView.hidden = NO;
|
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
self.roomTitleView.editable = NO;
|
2015-01-22 10:09:14 +00:00
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
|
2015-01-22 10:09:14 +00:00
|
|
|
[self.roomTitleView refreshDisplay];
|
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
#pragma mark -
|
2015-01-28 10:30:15 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
- (void)displayRoom:(MXKRoomDataSource*)roomDataSource {
|
|
|
|
[super displayRoom:roomDataSource];
|
|
|
|
|
|
|
|
[self updateUI];
|
2015-01-28 10:30:15 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
#pragma mark - MXKDataSource delegate
|
2014-10-15 23:34:46 +00:00
|
|
|
|
2015-03-31 15:51:25 +00:00
|
|
|
- (void)dataSource:(MXKDataSource *)dataSource didStateChange:(MXKDataSourceState)state {
|
|
|
|
// Take into account dataSource state to update UI
|
|
|
|
[self updateUI];
|
2015-03-31 13:09:17 +00:00
|
|
|
|
2015-03-31 15:51:25 +00:00
|
|
|
if ([super.class respondsToSelector:@selector(dataSource:didStateChange:)]) {
|
|
|
|
[super dataSource:dataSource didStateChange:state];
|
2014-10-16 20:17:00 +00:00
|
|
|
}
|
2014-10-15 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
- (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)actionIdentifier inCell:(id<MXKCellRendering>)cell userInfo:(NSDictionary *)userInfo {
|
|
|
|
|
|
|
|
// Override default implementation in case of tap on avatar
|
|
|
|
if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnAvatarView]) {
|
|
|
|
selectedRoomMember = [self.dataSource.room.state memberWithUserId:userInfo[kMXKRoomBubbleCellUserIdKey]];
|
|
|
|
if (selectedRoomMember) {
|
|
|
|
[self performSegueWithIdentifier:@"showMemberSheet" sender:self];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Keep default implementation for other actions
|
|
|
|
[super dataSource:dataSource didRecognizeAction:actionIdentifier inCell:cell userInfo:userInfo];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//#pragma mark - Internal methods
|
|
|
|
|
|
|
|
//- (void)configureView {
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//// TODO GFO review following observer use
|
|
|
|
//// [[AppSettings sharedSettings] addObserver:self forKeyPath:@"hideRedactions" options:0 context:nil];
|
|
|
|
//// [[AppSettings sharedSettings] addObserver:self forKeyPath:@"hideUnsupportedEvents" options:0 context:nil];
|
|
|
|
//// [mxHandler addObserver:self forKeyPath:@"isActivityInProgress" options:0 context:nil];
|
|
|
|
//}
|
|
|
|
|
|
|
|
//#pragma mark - KVO
|
|
|
|
|
|
|
|
//- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
|
|
|
// if ([@"isActivityInProgress" isEqualToString:keyPath]) {
|
|
|
|
// if ([MatrixSDKHandler sharedHandler].isActivityInProgress) {
|
|
|
|
// [self startActivityIndicator];
|
|
|
|
// } else {
|
|
|
|
// [self stopActivityIndicator];
|
|
|
|
// }
|
|
|
|
// } else if ([@"hideUnsupportedEvents" isEqualToString:keyPath] || [@"hideRedactions" isEqualToString:keyPath]) {
|
|
|
|
// dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
// if (nil == _mxRoom) {
|
|
|
|
// // The view controller has been released. Not need to go further
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
// [self configureView];
|
|
|
|
// });
|
|
|
|
// }
|
|
|
|
//}
|
2015-01-30 17:47:43 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
# pragma mark - Room members
|
2014-11-18 13:25:59 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
- (void)updateRoomMembers {
|
|
|
|
|
|
|
|
NSArray* membersList = [self.dataSource.room.state members];
|
|
|
|
|
|
|
|
if (![[AppSettings sharedSettings] displayLeftUsers]) {
|
|
|
|
NSMutableArray* filteredMembers = [[NSMutableArray alloc] init];
|
|
|
|
|
|
|
|
for (MXRoomMember* member in membersList) {
|
|
|
|
if (member.membership != MXMembershipLeave) {
|
|
|
|
[filteredMembers addObject:member];
|
|
|
|
}
|
2014-11-18 13:25:59 +00:00
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
|
|
|
|
membersList = filteredMembers;
|
2014-11-18 13:25:59 +00:00
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
|
|
|
|
members = [membersList sortedArrayUsingComparator:^NSComparisonResult(MXRoomMember *member1, MXRoomMember *member2) {
|
|
|
|
// Move banned and left members at the end of the list
|
|
|
|
if (member1.membership == MXMembershipLeave || member1.membership == MXMembershipBan) {
|
|
|
|
if (member2.membership != MXMembershipLeave && member2.membership != MXMembershipBan) {
|
|
|
|
return NSOrderedDescending;
|
2015-02-03 14:34:32 +00:00
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
} else if (member2.membership == MXMembershipLeave || member2.membership == MXMembershipBan) {
|
|
|
|
return NSOrderedAscending;
|
2015-01-08 14:40:05 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
// Move invited members just before left and banned members
|
|
|
|
if (member1.membership == MXMembershipInvite) {
|
|
|
|
if (member2.membership != MXMembershipInvite) {
|
|
|
|
return NSOrderedDescending;
|
|
|
|
}
|
|
|
|
} else if (member2.membership == MXMembershipInvite) {
|
|
|
|
return NSOrderedAscending;
|
2015-02-03 14:34:32 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
if ([[AppSettings sharedSettings] sortMembersUsingLastSeenTime]) {
|
|
|
|
// Get the users that correspond to these members
|
|
|
|
MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler];
|
|
|
|
MXUser *user1 = [mxHandler.mxSession userWithUserId:member1.userId];
|
|
|
|
MXUser *user2 = [mxHandler.mxSession userWithUserId:member2.userId];
|
2015-01-08 14:40:05 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
// Move users who are not online or unavailable at the end (before invited users)
|
|
|
|
if ((user1.presence == MXPresenceOnline) || (user1.presence == MXPresenceUnavailable)) {
|
|
|
|
if ((user2.presence != MXPresenceOnline) && (user2.presence != MXPresenceUnavailable)) {
|
|
|
|
return NSOrderedAscending;
|
|
|
|
}
|
|
|
|
} else if ((user2.presence == MXPresenceOnline) || (user2.presence == MXPresenceUnavailable)) {
|
|
|
|
return NSOrderedDescending;
|
|
|
|
} else {
|
|
|
|
// Here both users are neither online nor unavailable (the lastActive ago is useless)
|
|
|
|
// We will sort them according to their display, by keeping in front the offline users
|
|
|
|
if (user1.presence == MXPresenceOffline) {
|
|
|
|
if (user2.presence != MXPresenceOffline) {
|
|
|
|
return NSOrderedAscending;
|
2015-02-03 14:34:32 +00:00
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
} else if (user2.presence == MXPresenceOffline) {
|
|
|
|
return NSOrderedDescending;
|
2015-02-03 14:34:32 +00:00
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
return [[self.dataSource.room.state memberSortedName:member1.userId] compare:[self.dataSource.room.state memberSortedName:member2.userId] options:NSCaseInsensitiveSearch];
|
2015-02-03 14:34:32 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
// Consider user's lastActive ago value
|
|
|
|
if (user1.lastActiveAgo < user2.lastActiveAgo) {
|
|
|
|
return NSOrderedAscending;
|
|
|
|
} else if (user1.lastActiveAgo == user2.lastActiveAgo) {
|
|
|
|
return [[self.dataSource.room.state memberSortedName:member1.userId] compare:[self.dataSource.room.state memberSortedName:member2.userId] options:NSCaseInsensitiveSearch];
|
|
|
|
}
|
|
|
|
return NSOrderedDescending;
|
|
|
|
} else {
|
|
|
|
// Move user without display name at the end (before invited users)
|
|
|
|
if (member1.displayname.length) {
|
|
|
|
if (!member2.displayname.length) {
|
|
|
|
return NSOrderedAscending;
|
2015-02-19 12:08:43 +00:00
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
} else if (member2.displayname.length) {
|
|
|
|
return NSOrderedDescending;
|
2015-02-03 14:34:32 +00:00
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
|
|
|
|
return [[self.dataSource.room.state memberSortedName:member1.userId] compare:[self.dataSource.room.state memberSortedName:member2.userId] options:NSCaseInsensitiveSearch];
|
2015-01-08 14:40:05 +00:00
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
}];
|
2015-01-28 10:30:15 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
self.showRoomMembersButtonItem.enabled = (members.count != 0);
|
2015-01-28 10:30:15 +00:00
|
|
|
}
|
2015-01-08 14:40:05 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
- (IBAction)showRoomMembers:(id)sender {
|
|
|
|
// Dismiss keyboard
|
|
|
|
[self dismissKeyboard];
|
2015-01-16 09:42:36 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
[self updateRoomMembers];
|
2014-10-15 23:34:46 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
// check if there is some members to display
|
|
|
|
// else it makes no sense to display the list
|
|
|
|
if (0 == members.count) {
|
|
|
|
return;
|
2014-10-15 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
self.membersView.hidden = NO;
|
|
|
|
[self.membersTableView reloadData];
|
2014-12-15 15:54:31 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
// Update navigation bar items
|
|
|
|
self.navigationItem.hidesBackButton = YES;
|
|
|
|
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(hideRoomMembers:)];
|
2014-10-15 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
- (IBAction)hideRoomMembers:(id)sender {
|
|
|
|
self.membersView.hidden = YES;
|
|
|
|
members = nil;
|
2015-02-17 09:01:55 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
// Update navigation bar items
|
|
|
|
self.navigationItem.hidesBackButton = NO;
|
|
|
|
self.navigationItem.rightBarButtonItem = _showRoomMembersButtonItem;
|
|
|
|
|
|
|
|
// Force a reload to release all table cells (and then stop running timer)
|
|
|
|
[self.membersTableView reloadData];
|
2014-10-16 15:04:30 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
#pragma mark - UITableView data source
|
2015-01-16 15:39:58 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
|
|
|
return 1;
|
2015-01-16 15:39:58 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
|
|
|
// Check table view members vs messages
|
|
|
|
if (tableView == self.membersTableView) {
|
|
|
|
return members.count;
|
2015-01-16 15:39:58 +00:00
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
return 0;
|
2015-01-16 15:39:58 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
|
|
// Check table view members vs messages
|
|
|
|
if (tableView == self.membersTableView) {
|
|
|
|
// Use the same default height than message cell
|
|
|
|
return 50;
|
2014-11-05 08:01:14 +00:00
|
|
|
}
|
2014-11-27 09:50:09 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
|
|
MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler];
|
|
|
|
|
|
|
|
// Check table view members vs messages
|
|
|
|
if (tableView == self.membersTableView) {
|
|
|
|
RoomMemberTableCell *memberCell = [tableView dequeueReusableCellWithIdentifier:@"RoomMemberCell" forIndexPath:indexPath];
|
|
|
|
if (indexPath.row < members.count) {
|
|
|
|
MXRoomMember *roomMember = [members objectAtIndex:indexPath.row];
|
|
|
|
[memberCell setRoomMember:roomMember withRoom:self.dataSource.room];
|
|
|
|
if ([roomMember.userId isEqualToString:mxHandler.userId]) {
|
|
|
|
memberCell.typingBadge.hidden = YES; //hide typing badge for the current user
|
|
|
|
} else {
|
|
|
|
memberCell.typingBadge.hidden = YES; //TODO ([currentTypingUsers indexOfObject:roomMember.userId] == NSNotFound);
|
|
|
|
if (!memberCell.typingBadge.hidden) {
|
|
|
|
[memberCell.typingBadge.superview bringSubviewToFront:memberCell.typingBadge];
|
2014-11-28 09:51:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
return memberCell;
|
2014-11-28 09:51:22 +00:00
|
|
|
}
|
2015-01-14 16:51:53 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
return nil;
|
2014-11-05 08:01:14 +00:00
|
|
|
}
|
|
|
|
|
2014-11-18 13:25:59 +00:00
|
|
|
#pragma mark - UITableView delegate
|
2014-10-14 15:56:03 +00:00
|
|
|
|
|
|
|
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
2014-11-07 14:54:37 +00:00
|
|
|
if (tableView == self.membersTableView) {
|
|
|
|
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
2015-03-31 13:09:17 +00:00
|
|
|
} else {
|
|
|
|
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
|
2014-10-28 14:45:10 +00:00
|
|
|
}
|
2014-10-14 15:56:03 +00:00
|
|
|
}
|
|
|
|
|
2015-01-21 10:18:03 +00:00
|
|
|
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath {
|
|
|
|
// Release here resources, and restore reusable cells
|
|
|
|
|
|
|
|
// Check table view members vs messages
|
2015-02-18 16:06:53 +00:00
|
|
|
if ([cell isKindOfClass:[RoomMemberTableCell class]]) {
|
2015-01-21 10:18:03 +00:00
|
|
|
RoomMemberTableCell *memberCell = (RoomMemberTableCell*)cell;
|
|
|
|
// Stop potential timer used to refresh member's presence
|
|
|
|
[memberCell setRoomMember:nil withRoom:nil];
|
2015-03-31 13:09:17 +00:00
|
|
|
} else {
|
|
|
|
[super tableView:tableView didEndDisplayingCell:cell forRowAtIndexPath:indexPath];
|
2014-10-13 16:53:33 +00:00
|
|
|
}
|
|
|
|
|
2015-01-14 14:17:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - UITextField delegate
|
|
|
|
|
2014-11-21 17:56:12 +00:00
|
|
|
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
|
2014-12-15 15:54:31 +00:00
|
|
|
NSString *alertMsg = nil;
|
|
|
|
|
|
|
|
if (textField == _roomTitleView.displayNameTextField) {
|
2014-12-08 14:48:35 +00:00
|
|
|
// Check whether the user has enough power to rename the room
|
2015-03-31 13:09:17 +00:00
|
|
|
MXRoomPowerLevels *powerLevels = [self.dataSource.room.state powerLevels];
|
2015-01-23 14:36:05 +00:00
|
|
|
NSUInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:[MatrixSDKHandler sharedHandler].userId];
|
2014-12-22 09:17:08 +00:00
|
|
|
if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomName]) {
|
2014-12-12 13:58:53 +00:00
|
|
|
// Only the room name is edited here, update the text field with the room name
|
2015-03-31 13:09:17 +00:00
|
|
|
textField.text = self.dataSource.room.state.name;
|
2014-12-15 15:54:31 +00:00
|
|
|
textField.backgroundColor = [UIColor whiteColor];
|
2014-12-08 14:48:35 +00:00
|
|
|
} else {
|
2014-12-15 15:54:31 +00:00
|
|
|
alertMsg = @"You are not authorized to edit this room name";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check whether the user is allowed to change room topic
|
2014-12-22 09:17:08 +00:00
|
|
|
if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomTopic]) {
|
2014-12-15 15:54:31 +00:00
|
|
|
// Show topic text field even if the current value is nil
|
|
|
|
_roomTitleView.hiddenTopic = NO;
|
|
|
|
if (alertMsg) {
|
|
|
|
// Here the user can only update the room topic, switch on room topic field (without displaying alert)
|
|
|
|
alertMsg = nil;
|
|
|
|
[_roomTitleView.topicTextField becomeFirstResponder];
|
|
|
|
return NO;
|
2014-12-08 14:48:35 +00:00
|
|
|
}
|
|
|
|
}
|
2014-12-15 15:54:31 +00:00
|
|
|
} else if (textField == _roomTitleView.topicTextField) {
|
|
|
|
// Check whether the user has enough power to edit room topic
|
2015-03-31 13:09:17 +00:00
|
|
|
MXRoomPowerLevels *powerLevels = [self.dataSource.room.state powerLevels];
|
2015-01-23 14:36:05 +00:00
|
|
|
NSUInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:[MatrixSDKHandler sharedHandler].userId];
|
2014-12-22 09:17:08 +00:00
|
|
|
if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomTopic]) {
|
2014-12-15 15:54:31 +00:00
|
|
|
textField.backgroundColor = [UIColor whiteColor];
|
2014-12-19 07:03:39 +00:00
|
|
|
[self.roomTitleView stopTopicAnimation];
|
2014-12-15 15:54:31 +00:00
|
|
|
} else {
|
|
|
|
alertMsg = @"You are not authorized to edit this room topic";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (alertMsg) {
|
|
|
|
// Alert user
|
|
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
if (self.actionMenu) {
|
|
|
|
[self.actionMenu dismiss:NO];
|
|
|
|
}
|
2015-01-23 12:46:27 +00:00
|
|
|
self.actionMenu = [[MXCAlert alloc] initWithTitle:nil message:alertMsg style:MXCAlertStyleAlert];
|
|
|
|
self.actionMenu.cancelButtonIndex = [self.actionMenu addActionWithTitle:@"Cancel" style:MXCAlertActionStyleDefault handler:^(MXCAlert *alert) {
|
2014-12-15 15:54:31 +00:00
|
|
|
weakSelf.actionMenu = nil;
|
|
|
|
}];
|
|
|
|
[self.actionMenu showInViewController:self];
|
2014-12-08 14:48:35 +00:00
|
|
|
return NO;
|
2014-11-21 17:56:12 +00:00
|
|
|
}
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)textFieldDidEndEditing:(UITextField *)textField {
|
2014-12-15 15:54:31 +00:00
|
|
|
if (textField == _roomTitleView.displayNameTextField) {
|
|
|
|
textField.backgroundColor = [UIColor clearColor];
|
2014-12-08 14:48:35 +00:00
|
|
|
|
2014-12-15 15:54:31 +00:00
|
|
|
NSString *roomName = textField.text;
|
2015-03-31 13:09:17 +00:00
|
|
|
if ((roomName.length || self.dataSource.room.state.name.length) && [roomName isEqualToString:self.dataSource.room.state.name] == NO) {
|
|
|
|
[self.activityIndicator startAnimating];
|
2014-12-11 17:37:25 +00:00
|
|
|
__weak typeof(self) weakSelf = self;
|
2015-03-31 13:09:17 +00:00
|
|
|
[self.dataSource.room setName:roomName success:^{
|
|
|
|
// Here the activityIndicator should be stopped, we call `didMatrixSessionStateChange` to take
|
|
|
|
// into account mxSession state before stopping activity indicator.
|
|
|
|
[super didMatrixSessionStateChange];
|
2014-12-15 15:54:31 +00:00
|
|
|
// Refresh title display
|
2015-03-31 13:09:17 +00:00
|
|
|
textField.text = weakSelf.dataSource.room.state.displayname;
|
2014-11-21 17:56:12 +00:00
|
|
|
} failure:^(NSError *error) {
|
2015-03-31 13:09:17 +00:00
|
|
|
[super didMatrixSessionStateChange];
|
2014-11-21 17:56:12 +00:00
|
|
|
// Revert change
|
2015-03-31 13:09:17 +00:00
|
|
|
textField.text = weakSelf.dataSource.room.state.displayname;
|
|
|
|
NSLog(@"[Console RoomVC] Rename room failed: %@", error);
|
|
|
|
// Alert user
|
2014-11-21 17:56:12 +00:00
|
|
|
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
|
|
|
}];
|
2014-12-12 13:58:53 +00:00
|
|
|
} else {
|
|
|
|
// No change on room name, restore title with room displayName
|
2015-03-31 13:09:17 +00:00
|
|
|
textField.text = self.dataSource.room.state.displayname;
|
2014-12-15 15:54:31 +00:00
|
|
|
}
|
|
|
|
} else if (textField == _roomTitleView.topicTextField) {
|
|
|
|
textField.backgroundColor = [UIColor clearColor];
|
|
|
|
|
|
|
|
NSString *topic = textField.text;
|
2015-03-31 13:09:17 +00:00
|
|
|
if ((topic.length || self.dataSource.room.state.topic.length) && [topic isEqualToString:self.dataSource.room.state.topic] == NO) {
|
|
|
|
[self.activityIndicator startAnimating];
|
2014-12-15 15:54:31 +00:00
|
|
|
__weak typeof(self) weakSelf = self;
|
2015-03-31 13:09:17 +00:00
|
|
|
[self.dataSource.room setTopic:topic success:^{
|
|
|
|
// Here the activityIndicator should be stopped, we call `didMatrixSessionStateChange` to take
|
|
|
|
// into account mxSession state before stopping activity indicator.
|
|
|
|
[super didMatrixSessionStateChange];
|
2014-12-15 15:54:31 +00:00
|
|
|
// Hide topic field if empty
|
|
|
|
weakSelf.roomTitleView.hiddenTopic = !textField.text.length;
|
|
|
|
} failure:^(NSError *error) {
|
2015-03-31 13:09:17 +00:00
|
|
|
[super didMatrixSessionStateChange];
|
2014-12-15 15:54:31 +00:00
|
|
|
// Revert change
|
2015-03-31 13:09:17 +00:00
|
|
|
textField.text = weakSelf.dataSource.room.state.topic;
|
2014-12-15 15:54:31 +00:00
|
|
|
// Hide topic field if empty
|
|
|
|
weakSelf.roomTitleView.hiddenTopic = !textField.text.length;
|
2015-02-24 12:49:05 +00:00
|
|
|
NSLog(@"[RoomVC] Topic room change failed: %@", error);
|
2014-12-15 15:54:31 +00:00
|
|
|
//Alert user
|
|
|
|
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
|
|
|
}];
|
|
|
|
} else {
|
|
|
|
// Hide topic field if empty
|
|
|
|
_roomTitleView.hiddenTopic = !topic.length;
|
2014-11-21 17:56:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-12-08 14:48:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)textFieldShouldReturn:(UITextField*) textField {
|
2014-12-15 15:54:31 +00:00
|
|
|
if (textField == _roomTitleView.displayNameTextField) {
|
|
|
|
// "Next" key has been pressed
|
|
|
|
[_roomTitleView.topicTextField becomeFirstResponder];
|
|
|
|
} else {
|
|
|
|
// "Done" key has been pressed
|
|
|
|
[textField resignFirstResponder];
|
|
|
|
}
|
2014-10-13 16:53:33 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2014-12-19 13:16:22 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
// TODO GFO move this method into dataSource
|
|
|
|
- (BOOL)isIRCStyleCommand:(NSString*)string {
|
|
|
|
// Override the default behavior for `/join` command in order to open automatically the joined room
|
2014-10-27 10:35:52 +00:00
|
|
|
|
2015-03-31 13:09:17 +00:00
|
|
|
if ([string hasPrefix:kCmdJoinRoom]) {
|
2014-10-27 10:35:52 +00:00
|
|
|
// Join a room
|
2015-03-31 13:09:17 +00:00
|
|
|
NSString *roomAlias = [string substringFromIndex:kCmdJoinRoom.length + 1];
|
2014-10-27 10:35:52 +00:00
|
|
|
// Remove white space from both ends
|
|
|
|
roomAlias = [roomAlias stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
|
|
|
|
|
|
|
// Check
|
|
|
|
if (roomAlias.length) {
|
2015-01-23 14:36:05 +00:00
|
|
|
[[MatrixSDKHandler sharedHandler].mxSession joinRoom:roomAlias success:^(MXRoom *room) {
|
2014-12-17 09:55:33 +00:00
|
|
|
// Show the room
|
|
|
|
[[AppDelegate theDelegate].masterTabBarController showRoom:room.state.roomId];
|
|
|
|
} failure:^(NSError *error) {
|
2015-03-31 13:09:17 +00:00
|
|
|
NSLog(@"[Console RoomVC] Join roomAlias (%@) failed: %@", roomAlias, error);
|
2014-12-17 09:55:33 +00:00
|
|
|
//Alert user
|
|
|
|
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
|
|
|
}];
|
2014-10-27 10:35:52 +00:00
|
|
|
} else {
|
|
|
|
// Display cmd usage in text input as placeholder
|
2015-03-31 13:09:17 +00:00
|
|
|
self.inputToolbarView.placeholder = @"Usage: /join <room_alias>";
|
2014-11-17 16:32:46 +00:00
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
return YES;
|
2014-11-04 16:26:33 +00:00
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
return [super isIRCStyleCommand:string];
|
2014-11-04 16:26:33 +00:00
|
|
|
}
|
2014-12-23 17:51:49 +00:00
|
|
|
|
|
|
|
#pragma mark - Segues
|
|
|
|
|
|
|
|
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
|
|
|
if ([[segue identifier] isEqualToString:@"showMemberSheet"]) {
|
|
|
|
MemberViewController* controller = [segue destinationViewController];
|
2015-01-28 10:30:15 +00:00
|
|
|
|
|
|
|
if (selectedRoomMember) {
|
|
|
|
controller.mxRoomMember = selectedRoomMember;
|
|
|
|
selectedRoomMember = nil;
|
|
|
|
} else {
|
|
|
|
NSIndexPath *indexPath = [self.membersTableView indexPathForSelectedRow];
|
|
|
|
controller.mxRoomMember = [members objectAtIndex:indexPath.row];
|
|
|
|
}
|
2015-03-31 13:09:17 +00:00
|
|
|
controller.mxRoom = self.dataSource.room;
|
2014-12-23 17:51:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-02 15:02:47 +00:00
|
|
|
@end
|
2014-12-22 10:55:28 +00:00
|
|
|
|
|
|
|
|