Files
bundesmessenger-ios/syMessaging/syMessaging/ViewController/RoomViewController.m
T
2014-11-10 09:40:24 +01:00

1282 lines
58 KiB
Objective-C

/*
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.
*/
#import <MobileCoreServices/MobileCoreServices.h>
#import "RoomViewController.h"
#import "RoomMessageTableCell.h"
#import "RoomMemberTableCell.h"
#import "MatrixHandler.h"
#import "AppDelegate.h"
#import "AppSettings.h"
#import "MediaManager.h"
#define ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH 200
#define ROOM_MESSAGE_CELL_TOP_MARGIN 5
#define ROOM_MESSAGE_CELL_BOTTOM_MARGIN 5
#define INCOMING_MESSAGE_CELL_USER_LABEL_HEIGHT 20
#define ROOM_MESSAGE_CELL_IMAGE_MARGIN 5
NSString *const kCmdChangeDisplayName = @"/nick";
NSString *const kCmdEmote = @"/me";
NSString *const kCmdJoinRoom = @"/join";
NSString *const kCmdKickUser = @"/kick";
NSString *const kCmdBanUser = @"/ban";
NSString *const kCmdUnbanUser = @"/unban";
NSString *const kCmdSetUserPowerLevel = @"/op";
NSString *const kCmdResetUserPowerLevel = @"/deop";
NSString *const kLocalEchoEventIdPrefix = @"localEcho-";
NSString *const kFailedEventId = @"failedEventId";
@interface RoomViewController ()
{
BOOL isFirstDisplay;
BOOL isJoinRequestInProgress;
MXRoom *mxRoom;
NSMutableArray *messages;
id messagesListener;
// Members list
NSArray *members;
id membersListener;
// Cache
NSMutableArray *tmpCachedAttachments;
}
@property (weak, nonatomic) IBOutlet UINavigationItem *roomNavItem;
@property (weak, nonatomic) IBOutlet UITableView *messagesTableView;
@property (weak, nonatomic) IBOutlet UIView *controlView;
@property (weak, nonatomic) IBOutlet UIButton *optionBtn;
@property (weak, nonatomic) IBOutlet UITextField *messageTextField;
@property (weak, nonatomic) IBOutlet UIButton *sendBtn;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *controlViewBottomConstraint;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
@property (weak, nonatomic) IBOutlet UIView *membersView;
@property (weak, nonatomic) IBOutlet UITableView *membersTableView;
@property (strong, nonatomic) CustomAlert *actionMenu;
@end
@implementation RoomViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
isFirstDisplay = YES;
UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoLight];
[button addTarget:self action:@selector(showHideRoomMembers:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
_sendBtn.enabled = NO;
_sendBtn.alpha = 0.5;
}
- (void)dealloc {
#ifdef TEMPORARY_PATCH_INITIAL_SYNC
// FIXME: these lines should be removed when SDK will fix the initial sync issue
if (isJoinRequestInProgress) {
[[MatrixHandler sharedHandler] removeObserver:self forKeyPath:@"isInitialSyncDone"];
}
#endif
// Clear temporary cached attachments (used for local echo)
NSUInteger index = tmpCachedAttachments.count;
while (index--) {
[MediaManager clearCacheForURL:[tmpCachedAttachments objectAtIndex:index]];
}
tmpCachedAttachments = nil;
messages = nil;
if (messagesListener) {
[mxRoom unregisterListener:messagesListener];
messagesListener = nil;
}
mxRoom = nil;
members = nil;
if (membersListener) {
membersListener = nil;
}
if (_actionMenu) {
[_actionMenu dismiss:NO];
_actionMenu = nil;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onKeyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onKeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onTextFieldChange:) name:UITextFieldTextDidChangeNotification object:nil];
// Set visible room id
[AppDelegate theDelegate].masterTabBarController.visibleRoomId = self.roomId;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// hide action
if (_actionMenu) {
[_actionMenu dismiss:NO];
_actionMenu = nil;
}
// Hide members by default
[self hideRoomMembers];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil];
// Reset visible room id
[AppDelegate theDelegate].masterTabBarController.visibleRoomId = nil;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (isFirstDisplay) {
// Scroll to the bottom
[self scrollToBottomAnimated:animated];
isFirstDisplay = NO;
}
}
#pragma mark -
- (void)setRoomId:(NSString *)roomId {
_roomId = roomId;
// Load room data
[self configureView];
// Trigger a back pagination if messages number is low
if (messages && messages.count < 10) {
[self triggerBackPagination];
}
}
#ifdef TEMPORARY_PATCH_INITIAL_SYNC
// FIXME: this method should be removed when SDK will fix the initial sync issue
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([@"isInitialSyncDone" isEqualToString:keyPath])
{
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
if ([mxHandler isInitialSyncDone]) {
[_activityIndicator stopAnimating];
isJoinRequestInProgress = NO;
[mxHandler removeObserver:self forKeyPath:@"isInitialSyncDone"];
dispatch_async(dispatch_get_main_queue(), ^{
[self configureView];
});
}
}
}
#endif
#pragma mark - Internal methods
- (void)configureView {
// Check whether a request is in progress to join the room
if (isJoinRequestInProgress) {
// Busy - be sure that activity indicator is running
[_activityIndicator startAnimating];
return;
}
// Flush messages
messages = nil;
// Remove potential roomData listener
if (messagesListener && mxRoom) {
[mxRoom unregisterListener:messagesListener];
messagesListener = nil;
}
// Update room data
if (self.roomId) {
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
mxRoom = [mxHandler.mxSession room:self.roomId];
// Update room title
self.roomNavItem.title = mxRoom.displayname;
// Join the room if the user is not already listed in room's members
if ([mxRoom getMember:mxHandler.userId] == nil) {
isJoinRequestInProgress = YES;
[_activityIndicator startAnimating];
[mxHandler.mxRestClient joinRoom:self.roomId success:^{
#ifdef TEMPORARY_PATCH_INITIAL_SYNC
// Presently the SDK is not able to handle correctly the context for the room recently joined
// PATCH: we force new initial sync
// FIXME: this new initial sync should be removed when SDK will fix the issue
[mxHandler addObserver:self forKeyPath:@"isInitialSyncDone" options:0 context:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[mxHandler forceInitialSync];
});
#else
[_activityIndicator stopAnimating];
isJoinRequestInProgress = NO;
dispatch_async(dispatch_get_main_queue(), ^{
[self configureView];
});
#endif
} failure:^(NSError *error) {
[_activityIndicator stopAnimating];
isJoinRequestInProgress = NO;
NSLog(@"Failed to join room (%@): %@", mxRoom.displayname, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
return;
}
messages = [NSMutableArray arrayWithArray:mxRoom.messages];
// Register a listener for events that modify the `messages` property
messagesListener = [mxRoom registerEventListenerForTypes:mxHandler.mxSession.eventsFilterForMessages block:^(MXRoom *room, MXEvent *event, BOOL isLive) {
// consider only live event
if (isLive) {
// For outgoing message, remove the temporary event
if ([event.userId isEqualToString:[MatrixHandler sharedHandler].userId]) {
NSUInteger index = messages.count;
while (index--) {
MXEvent *mxEvent = [messages objectAtIndex:index];
if ([mxEvent.eventId isEqualToString:event.eventId]) {
[messages replaceObjectAtIndex:index withObject:event];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
return;
}
}
}
// Here a new event is added
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:messages.count inSection:0];
[messages addObject:event];
[self.messagesTableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];
[self scrollToBottomAnimated:YES];
}
}];
} else {
mxRoom = nil;
// Update room title
self.roomNavItem.title = nil;
}
[self.messagesTableView reloadData];
}
- (void)scrollToBottomAnimated:(BOOL)animated {
// Scroll table view to the bottom
NSInteger rowNb = messages.count;
if (rowNb) {
[self.messagesTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:(rowNb - 1) inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated];
}
}
- (void)triggerBackPagination {
if (mxRoom.canPaginate)
{
[_activityIndicator startAnimating];
[mxRoom paginateBackMessages:20 success:^(NSArray *oldMessages) {
if (oldMessages.count)
{
// Update table sources
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, oldMessages.count)];
[messages insertObjects:oldMessages atIndexes:indexSet];
// Prepare insertion of new rows at the top of the table (compute cumulative height of added cells)
NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:oldMessages.count];
CGFloat verticalOffset = 0;
for (NSUInteger index = 0; index < oldMessages.count; index++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[indexPaths addObject:indexPath];
verticalOffset += [self tableView:self.messagesTableView heightForRowAtIndexPath:indexPath];
}
// Disable animation during cells insertion to prevent flickering
[UIView setAnimationsEnabled:NO];
// Store the current content offset
CGPoint contentOffset = self.messagesTableView.contentOffset;
[self.messagesTableView beginUpdates];
[self.messagesTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
[self.messagesTableView endUpdates];
// Enable animation again
[UIView setAnimationsEnabled:YES];
// Fix vertical offset in order to prevent scrolling down
contentOffset.y += verticalOffset;
[self.messagesTableView setContentOffset:contentOffset animated:NO];
[_activityIndicator stopAnimating];
// Move the current message at the middle of the visible area (dispatch this action in order to let table end its refresh)
dispatch_async(dispatch_get_main_queue(), ^{
[self.messagesTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:(oldMessages.count - 1) inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
});
} else {
// Here there was no event related to the `messages` property
[_activityIndicator stopAnimating];
// Trigger a new back pagination (if possible)
[self triggerBackPagination];
}
} failure:^(NSError *error) {
[_activityIndicator stopAnimating];
NSLog(@"Failed to paginate back: %@", error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}
- (void)showHideRoomMembers:(id)sender {
// Check whether the members list is displayed
if (members) {
[self hideRoomMembers];
} else {
[self showRoomMembers];
}
}
- (void)updateRoomMembers {
members = [[mxRoom members] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
MXRoomMember *member1 = (MXRoomMember*)obj1;
MXRoomMember *member2 = (MXRoomMember*)obj2;
// 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;
}
} else if (member2.membership == MXMembershipLeave || member2.membership == MXMembershipBan) {
return NSOrderedAscending;
}
// 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;
}
if ([[AppSettings sharedSettings] sortMembersUsingLastSeenTime]) {
// Get the users that correspond to these members
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
MXUser *user1 = [mxHandler.mxSession user:member1.userId];
MXUser *user2 = [mxHandler.mxSession user:member2.userId];
if (user1.lastActiveAgo < user2.lastActiveAgo) {
return NSOrderedAscending;
} else if (user1.lastActiveAgo == user2.lastActiveAgo) {
return [[mxRoom memberName:member1.userId] compare:[mxRoom memberName:member2.userId] options:NSCaseInsensitiveSearch];
}
return NSOrderedDescending;
} else {
// Move user without display name at the end (before invited users)
if (member1.displayname) {
if (!member2.displayname) {
return NSOrderedAscending;
}
} else if (member2.displayname) {
return NSOrderedDescending;
}
return [[mxRoom memberName:member1.userId] compare:[mxRoom memberName:member2.userId] options:NSCaseInsensitiveSearch];
}
}];
}
- (void)showRoomMembers {
// Dismiss keyboard
[self dismissKeyboard];
[self updateRoomMembers];
// Register a listener for events that concern room members
NSArray *mxMembersEvents = @[
kMXEventTypeStringRoomMember,
kMXEventTypeStringRoomPowerLevels,
kMXEventTypeStringPresence
];
membersListener = [mxRoom registerEventListenerForTypes:mxMembersEvents block:^(MXRoom *room, MXEvent *event, BOOL isLive) {
// consider only live event
if (isLive) {
// Refresh members list
[self updateRoomMembers];
[self.membersTableView reloadData];
}
}];
self.membersView.hidden = NO;
[self.membersTableView reloadData];
}
- (void)hideRoomMembers {
if (membersListener) {
[mxRoom unregisterListener:membersListener];
membersListener = nil;
}
self.membersView.hidden = YES;
members = nil;
}
#pragma mark - keyboard handling
- (void)onKeyboardWillShow:(NSNotification *)notif {
NSValue *rectVal = notif.userInfo[UIKeyboardFrameEndUserInfoKey];
CGRect endRect = rectVal.CGRectValue;
UIEdgeInsets insets = self.messagesTableView.contentInset;
// Handle portrait/landscape mode
insets.bottom = (endRect.origin.y == 0) ? endRect.size.width : endRect.size.height;
self.messagesTableView.contentInset = insets;
[self scrollToBottomAnimated:YES];
// Move up control view
// Don't forget the offset related to tabBar
_controlViewBottomConstraint.constant = insets.bottom - [AppDelegate theDelegate].masterTabBarController.tabBar.frame.size.height;
}
- (void)onKeyboardWillHide:(NSNotification *)notif {
UIEdgeInsets insets = self.messagesTableView.contentInset;
insets.bottom = self.controlView.frame.size.height;
self.messagesTableView.contentInset = insets;
_controlViewBottomConstraint.constant = 0;
}
- (void)dismissKeyboard {
// Hide the keyboard
[_messageTextField resignFirstResponder];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Check table view members vs messages
if (tableView == self.membersTableView)
{
return members.count;
}
return messages.count;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// Check table view members vs messages
if (tableView == self.membersTableView)
{
return 50;
}
// Compute here height of message cells
CGFloat rowHeight;
// Get event related to this row
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
MXEvent *mxEvent = [messages objectAtIndex:indexPath.row];
BOOL isIncomingMsg = ([mxEvent.userId isEqualToString:mxHandler.userId] == NO);
CGSize contentSize;
if ([mxHandler isAttachment:mxEvent]) {
contentSize = [self attachmentContentSize:mxEvent];
} else {
// Use a TextView template to compute cell height
UITextView *dummyTextView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH, MAXFLOAT)];
dummyTextView.font = [UIFont systemFontOfSize:14];
dummyTextView.text = [mxHandler displayTextFor:mxEvent inSubtitleMode:NO];
contentSize = [dummyTextView sizeThatFits:dummyTextView.frame.size];
}
// Handle incoming / outgoing layout
if (isIncomingMsg) {
// By default the user name is displayed above the message or attachment
rowHeight = contentSize.height + ROOM_MESSAGE_CELL_TOP_MARGIN + INCOMING_MESSAGE_CELL_USER_LABEL_HEIGHT + ROOM_MESSAGE_CELL_BOTTOM_MARGIN;
if (indexPath.row) {
// This user name is hide if the previous message is from the same user
MXEvent *previousMxEvent = [messages objectAtIndex:indexPath.row - 1];
if ([previousMxEvent.userId isEqualToString:mxEvent.userId]) {
rowHeight -= INCOMING_MESSAGE_CELL_USER_LABEL_HEIGHT;
}
}
} else {
rowHeight = contentSize.height + ROOM_MESSAGE_CELL_TOP_MARGIN + ROOM_MESSAGE_CELL_BOTTOM_MARGIN;
}
// Force minimum height: 50
if (rowHeight < 50) {
rowHeight = 50;
}
return rowHeight;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
// Check table view members vs messages
if (tableView == self.membersTableView) {
RoomMemberTableCell *memberCell = [tableView dequeueReusableCellWithIdentifier:@"RoomMemberCell" forIndexPath:indexPath];
if (indexPath.row < members.count) {
[memberCell setRoomMember:[members objectAtIndex:indexPath.row] withRoom:mxRoom];
}
return memberCell;
}
// Handle here room message cells
RoomMessageTableCell *cell;
MXEvent *mxEvent = [messages objectAtIndex:indexPath.row];
BOOL isIncomingMsg = NO;
if ([mxEvent.userId isEqualToString:mxHandler.userId]) {
cell = [tableView dequeueReusableCellWithIdentifier:@"OutgoingMessageCell" forIndexPath:indexPath];
cell.messageTextView.backgroundColor = [UIColor groupTableViewBackgroundColor];
[((OutgoingMessageTableCell*)cell).activityIndicator stopAnimating];
} else {
cell = [tableView dequeueReusableCellWithIdentifier:@"IncomingMessageCell" forIndexPath:indexPath];
cell.messageTextView.backgroundColor = [UIColor lightGrayColor];
isIncomingMsg = YES;
}
// Clear background for notifications (We consider as notification mxEvent which is not a text message or an attachment)
if ([mxHandler isNotification:mxEvent]) {
cell.messageTextView.backgroundColor = [UIColor clearColor];
}
// Hide user picture if the previous message is from the same user
cell.pictureView.hidden = NO;
if (indexPath.row) {
MXEvent *previousMxEvent = [messages objectAtIndex:indexPath.row - 1];
if ([previousMxEvent.userId isEqualToString:mxEvent.userId]) {
cell.pictureView.hidden = YES;
}
}
// Set url for visible picture
if (!cell.pictureView.hidden) {
cell.placeholder = @"default-profile";
cell.pictureURL = [mxRoom getMember:mxEvent.userId].avatarUrl;
}
// Update incoming/outgoing message layout
if (isIncomingMsg) {
// Hide userName in incoming message if the previous message is from the same user
IncomingMessageTableCell* incomingMsgCell = (IncomingMessageTableCell*)cell;
CGRect frame = incomingMsgCell.userNameLabel.frame;
if (cell.pictureView.hidden) {
incomingMsgCell.userNameLabel.text = nil;
frame.size.height = 0;
incomingMsgCell.userNameLabel.hidden = YES;
} else {
frame.size.height = INCOMING_MESSAGE_CELL_USER_LABEL_HEIGHT;
incomingMsgCell.userNameLabel.hidden = NO;
NSString *userName = [mxRoom memberName:mxEvent.userId];
incomingMsgCell.userNameLabel.text = [NSString stringWithFormat:@"- %@", userName];
}
incomingMsgCell.userNameLabel.frame = frame;
// Reset text color
cell.messageTextView.textColor = [UIColor blackColor];
} else {
// Hide unsent label by default
UILabel *unsentLabel = ((OutgoingMessageTableCell*)cell).unsentLabel;
unsentLabel.hidden = YES;
// Set the right text color for outgoing messages
if ([mxEvent.eventId hasPrefix:kLocalEchoEventIdPrefix]) {
cell.messageTextView.textColor = [UIColor lightGrayColor];
} else if ([mxEvent.eventId hasPrefix:kFailedEventId]) {
cell.messageTextView.textColor = [UIColor redColor];
unsentLabel.hidden = NO;
} else {
cell.messageTextView.textColor = [UIColor blackColor];
}
}
if ([mxHandler isAttachment:mxEvent]) {
cell.attachmentView.hidden = NO;
cell.messageTextView.text = nil; // Note: Text view is used as attachment background view
CGSize contentSize = [self attachmentContentSize:mxEvent];
cell.msgTextViewWidthConstraint.constant = contentSize.width;
// Fade attachments during upload
if (isIncomingMsg == NO && [mxEvent.eventId hasPrefix:kLocalEchoEventIdPrefix]) {
cell.attachmentView.alpha = 0.5;
[((OutgoingMessageTableCell*)cell).activityIndicator startAnimating];
} else {
cell.attachmentView.alpha = 1;
}
NSString *msgtype = mxEvent.content[@"msgtype"];
if ([msgtype isEqualToString:kMXMessageTypeImage]) {
NSString *url = mxEvent.content[@"thumbnail_url"];
if (url == nil) {
url = mxEvent.content[@"url"];
}
cell.attachedImageURL = url;
} else {
cell.attachedImageURL = nil;
}
} else {
// Text message will be displayed in textView with max width
cell.msgTextViewWidthConstraint.constant = ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH;
// Cancel potential attachment loading
cell.attachedImageURL = nil;
cell.attachmentView.hidden = YES;
NSString *displayText = [mxHandler displayTextFor:mxEvent inSubtitleMode:NO];
// Update text color according to text content
if ([displayText hasPrefix:kMatrixHandlerUnsupportedMessagePrefix]) {
cell.messageTextView.textColor = [UIColor redColor];
} else if (isIncomingMsg && ([displayText rangeOfString:mxHandler.userDisplayName options:NSCaseInsensitiveSearch].location != NSNotFound || [displayText rangeOfString:mxHandler.userId options:NSCaseInsensitiveSearch].location != NSNotFound)) {
cell.messageTextView.textColor = [UIColor blueColor];
}
cell.messageTextView.text = displayText;
}
return cell;
}
- (CGSize)attachmentContentSize:(MXEvent*)mxEvent;
{
CGSize contentSize;
NSString *msgtype = mxEvent.content[@"msgtype"];
if ([msgtype isEqualToString:kMXMessageTypeImage]) {
CGFloat width, height;
width = height = ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH;
NSDictionary *thumbInfo = mxEvent.content[@"thumbnail_info"];
if (thumbInfo) {
width = [thumbInfo[@"w"] integerValue] + 2 * ROOM_MESSAGE_CELL_IMAGE_MARGIN;
height = [thumbInfo[@"h"] integerValue] + 2 * ROOM_MESSAGE_CELL_IMAGE_MARGIN;
if (width > ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH || height > ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH) {
if (width > height) {
height = (height * ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH) / width;
height = floorf(height / 2) * 2;
width = ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH;
} else {
width = (width * ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH) / height;
width = floorf(width / 2) * 2;
height = ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH;
}
}
}
contentSize = CGSizeMake(width, height);
} else {
contentSize = CGSizeMake(40, 40);
}
return contentSize;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Check table view members vs messages
if (tableView == self.membersTableView) {
// List action(s) available on this member
// TODO: Check user's power level before allowing an action (kick, ban, ...)
MXRoomMember *roomMember = [members objectAtIndex:indexPath.row];
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
__weak typeof(self) weakSelf = self;
if (_actionMenu) {
[_actionMenu dismiss:NO];
_actionMenu = nil;
}
// Consider the case of the user himself
if ([roomMember.userId isEqualToString:mxHandler.userId]) {
self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet];
[self.actionMenu addActionWithTitle:@"Leave" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
if (weakSelf) {
weakSelf.actionMenu = nil;
[[MatrixHandler sharedHandler].mxRestClient leaveRoom:weakSelf.roomId
success:^{
// Back to recents
[weakSelf.navigationController popViewControllerAnimated:YES];
}
failure:^(NSError *error) {
NSLog(@"Leave room %@ failed: %@", weakSelf.roomId, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}];
} else {
// Consider membership of the selected member
switch (roomMember.membership) {
case MXMembershipInvite:
case MXMembershipJoin: {
self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet];
[self.actionMenu addActionWithTitle:@"Kick" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
if (weakSelf) {
weakSelf.actionMenu = nil;
[[MatrixHandler sharedHandler].mxRestClient kickUser:roomMember.userId
fromRoom:weakSelf.roomId
reason:nil
success:^{
}
failure:^(NSError *error) {
NSLog(@"Kick %@ failed: %@", roomMember.userId, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}];
[self.actionMenu addActionWithTitle:@"Ban" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
if (weakSelf) {
weakSelf.actionMenu = nil;
[[MatrixHandler sharedHandler].mxRestClient banUser:roomMember.userId
inRoom:weakSelf.roomId
reason:nil
success:^{
}
failure:^(NSError *error) {
NSLog(@"Ban %@ failed: %@", roomMember.userId, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}];
break;
}
case MXMembershipLeave: {
self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet];
[self.actionMenu addActionWithTitle:@"Invite" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
if (weakSelf) {
weakSelf.actionMenu = nil;
[[MatrixHandler sharedHandler].mxRestClient inviteUser:roomMember.userId
toRoom:weakSelf.roomId
success:^{
}
failure:^(NSError *error) {
NSLog(@"Invite %@ failed: %@", roomMember.userId, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}];
[self.actionMenu addActionWithTitle:@"Ban" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
if (weakSelf) {
weakSelf.actionMenu = nil;
[[MatrixHandler sharedHandler].mxRestClient banUser:roomMember.userId
inRoom:weakSelf.roomId
reason:nil
success:^{
}
failure:^(NSError *error) {
NSLog(@"Ban %@ failed: %@", roomMember.userId, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}];
break;
}
case MXMembershipBan: {
self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet];
[self.actionMenu addActionWithTitle:@"Unban" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
if (weakSelf) {
weakSelf.actionMenu = nil;
[[MatrixHandler sharedHandler].mxRestClient unbanUser:roomMember.userId
inRoom:weakSelf.roomId
success:^{
}
failure:^(NSError *error) {
NSLog(@"Unban %@ failed: %@", roomMember.userId, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}];
break;
}
default: {
break;
}
}
}
// Display the action sheet (if any)
if (self.actionMenu) {
self.actionMenu.cancelButtonIndex = [self.actionMenu addActionWithTitle:@"Cancel" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
weakSelf.actionMenu = nil;
}];
[self.actionMenu showInViewController:self];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
} else if (tableView == self.messagesTableView) {
// Dismiss keyboard when user taps on messages table view content
[self dismissKeyboard];
}
}
// Detect vertical bounce at the top of the tableview to trigger pagination
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
if (scrollView == self.messagesTableView) {
// paginate ?
if ((scrollView.contentOffset.y < -64) && (_activityIndicator.isAnimating == NO))
{
[self triggerBackPagination];
}
}
}
#pragma mark - UITextField delegate
- (void)onTextFieldChange:(NSNotification *)notif {
NSString *msg = _messageTextField.text;
if (msg.length) {
_sendBtn.enabled = YES;
_sendBtn.alpha = 1;
// Reset potential placeholder (used in case of wrong command usage)
_messageTextField.placeholder = nil;
} else {
_sendBtn.enabled = NO;
_sendBtn.alpha = 0.5;
}
}
- (BOOL)textFieldShouldReturn:(UITextField*) textField {
// "Done" key has been pressed
[textField resignFirstResponder];
return YES;
}
#pragma mark - Actions
- (IBAction)onButtonPressed:(id)sender {
if (sender == _sendBtn) {
NSString *msgTxt = self.messageTextField.text;
// Handle potential commands in room chat
if ([self isIRCStyleCommand:msgTxt] == NO) {
[self postTextMessage:msgTxt];
}
self.messageTextField.text = nil;
// disable send button
[self onTextFieldChange:nil];
} else if (sender == _optionBtn) {
[self dismissKeyboard];
// Display action menu: Add attachments, Invite user...
__weak typeof(self) weakSelf = self;
self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet];
// Attachments
[self.actionMenu addActionWithTitle:@"Attach" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
if (weakSelf) {
// Ask for attachment type
weakSelf.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an attachment type:" message:nil style:CustomAlertStyleActionSheet];
[weakSelf.actionMenu addActionWithTitle:@"Media" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
if (weakSelf) {
// Open picture gallery
UIImagePickerController *mediaPicker = [[UIImagePickerController alloc] init];
mediaPicker.delegate = weakSelf;
mediaPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
mediaPicker.allowsEditing = NO;
mediaPicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, (NSString *)kUTTypeMovie, nil];
[[AppDelegate theDelegate].masterTabBarController presentMediaPicker:mediaPicker];
}
}];
weakSelf.actionMenu.cancelButtonIndex = [weakSelf.actionMenu addActionWithTitle:@"Cancel" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
weakSelf.actionMenu = nil;
}];
[weakSelf.actionMenu showInViewController:weakSelf];
}
}];
// Invitation
[self.actionMenu addActionWithTitle:@"Invite" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
if (weakSelf) {
// Ask for userId to invite
weakSelf.actionMenu = [[CustomAlert alloc] initWithTitle:@"User ID:" message:nil style:CustomAlertStyleAlert];
weakSelf.actionMenu.cancelButtonIndex = [weakSelf.actionMenu addActionWithTitle:@"Cancel" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
weakSelf.actionMenu = nil;
}];
[weakSelf.actionMenu addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = NO;
textField.placeholder = @"ex: @bob:homeserver";
}];
[weakSelf.actionMenu addActionWithTitle:@"Invite" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
UITextField *textField = [alert textFieldAtIndex:0];
NSString *userId = textField.text;
weakSelf.actionMenu = nil;
if (userId.length) {
[[MatrixHandler sharedHandler].mxRestClient inviteUser:userId toRoom:weakSelf.roomId success:^{
} failure:^(NSError *error) {
NSLog(@"Invite %@ failed: %@", userId, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}];
[weakSelf.actionMenu showInViewController:weakSelf];
}
}];
self.actionMenu.cancelButtonIndex = [self.actionMenu addActionWithTitle:@"Cancel" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
weakSelf.actionMenu = nil;
}];
weakSelf.actionMenu.sourceView = weakSelf.optionBtn;
[self.actionMenu showInViewController:self];
}
}
#pragma mark - Post messages
- (void)postMessage:(NSDictionary*)msgContent withLocalEventId:(NSString*)localEventId {
MXMessageType msgType = msgContent[@"msgtype"];
if (msgType) {
// Check whether a temporary event has already been added for local echo (this happens on attachments)
MXEvent *mxEvent = nil;
if (localEventId) {
// Update the temporary event with the actual msg content
NSUInteger index = messages.count;
while (index--) {
mxEvent = [messages objectAtIndex:index];
if ([mxEvent.eventId isEqualToString:localEventId]) {
mxEvent.content = msgContent;
// Refresh table display
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
break;
}
}
} else {
// Create a temporary event to displayed outgoing message (local echo)
localEventId = [NSString stringWithFormat:@"%@%@", kLocalEchoEventIdPrefix, [[NSProcessInfo processInfo] globallyUniqueString]];
mxEvent = [[MXEvent alloc] init];
mxEvent.roomId = self.roomId;
mxEvent.eventId = localEventId;
mxEvent.eventType = MXEventTypeRoomMessage;
mxEvent.type = kMXEventTypeStringRoomMessage;
mxEvent.content = msgContent;
mxEvent.userId = [MatrixHandler sharedHandler].userId;
// Update table sources
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:messages.count inSection:0];
[messages addObject:mxEvent];
// Refresh table display
[self.messagesTableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];
[self scrollToBottomAnimated:YES];
}
// Send message to the room
[[[MatrixHandler sharedHandler] mxRestClient] postMessage:self.roomId msgType:msgType content:mxEvent.content success:^(NSString *event_id) {
// Update the temporary event with the actual event id
NSUInteger index = messages.count;
while (index--) {
MXEvent *mxEvent = [messages objectAtIndex:index];
if ([mxEvent.eventId isEqualToString:localEventId]) {
mxEvent.eventId = event_id;
// Refresh table display
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
break;
}
}
} failure:^(NSError *error) {
NSLog(@"Failed to send message (%@): %@", mxEvent.description, error);
// Update the temporary event with the failed event id
NSUInteger index = messages.count;
while (index--) {
MXEvent *mxEvent = [messages objectAtIndex:index];
if ([mxEvent.eventId isEqualToString:localEventId]) {
mxEvent.eventId = kFailedEventId;
// Refresh table display
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self scrollToBottomAnimated:YES];
break;
}
}
}];
}
}
- (void)postTextMessage:(NSString*)msgTxt {
MXMessageType msgType = kMXMessageTypeText;
// Check whether the message is an emote
if ([msgTxt hasPrefix:@"/me "]) {
msgType = kMXMessageTypeEmote;
// Remove "/me " string
msgTxt = [msgTxt substringFromIndex:4];
}
[self postMessage:@{@"msgtype":msgType, @"body":msgTxt} withLocalEventId:nil];
}
- (BOOL)isIRCStyleCommand:(NSString*)text{
// Check whether the provided text may be an IRC-style command
if ([text hasPrefix:@"/"] == NO || [text hasPrefix:@"//"] == YES) {
return NO;
}
// Parse command line
NSArray *components = [text componentsSeparatedByString:@" "];
NSString *cmd = [components objectAtIndex:0];
NSUInteger index = 1;
if ([cmd isEqualToString:kCmdEmote]) {
// post message as an emote
[self postTextMessage:text];
} else if ([text hasPrefix:kCmdChangeDisplayName]) {
// Change display name
NSString *displayName = [text substringFromIndex:kCmdChangeDisplayName.length + 1];
// Remove white space from both ends
displayName = [displayName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (displayName.length) {
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
[mxHandler.mxRestClient setDisplayName:displayName success:^{
} failure:^(NSError *error) {
NSLog(@"Set displayName failed: %@", error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
} else {
// Display cmd usage in text input as placeholder
self.messageTextField.placeholder = @"Usage: /nick <display_name>";
}
} else if ([text hasPrefix:kCmdJoinRoom]) {
// Join a room
NSString *roomAlias = [text substringFromIndex:kCmdJoinRoom.length + 1];
// Remove white space from both ends
roomAlias = [roomAlias stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
// Check
if (roomAlias.length) {
// FIXME
NSLog(@"Join Alias is not supported yet (%@)", text);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"/join is not supported yet" message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
} else {
// Display cmd usage in text input as placeholder
self.messageTextField.placeholder = @"Usage: /join <room_alias>";
}
} else {
// Retrieve userId
NSString *userId = nil;
while (index < components.count) {
userId = [components objectAtIndex:index++];
if (userId.length) {
// done
break;
}
// reset
userId = nil;
}
if ([cmd isEqualToString:kCmdKickUser]) {
if (userId) {
// Retrieve potential reason
NSString *reason = nil;
while (index < components.count) {
if (reason) {
reason = [NSString stringWithFormat:@"%@ %@", reason, [components objectAtIndex:index++]];
} else {
reason = [components objectAtIndex:index++];
}
}
// Kick the user
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
[mxHandler.mxRestClient kickUser:userId fromRoom:self.roomId reason:reason success:^{
} failure:^(NSError *error) {
NSLog(@"Kick user (%@) failed: %@", userId, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
} else {
// Display cmd usage in text input as placeholder
self.messageTextField.placeholder = @"Usage: /kick <userId> [<reason>]";
}
} else if ([cmd isEqualToString:kCmdBanUser]) {
if (userId) {
// Retrieve potential reason
NSString *reason = nil;
while (index < components.count) {
if (reason) {
reason = [NSString stringWithFormat:@"%@ %@", reason, [components objectAtIndex:index++]];
} else {
reason = [components objectAtIndex:index++];
}
}
// Ban the user
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
[mxHandler.mxRestClient banUser:userId inRoom:self.roomId reason:reason success:^{
} failure:^(NSError *error) {
NSLog(@"Ban user (%@) failed: %@", userId, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
} else {
// Display cmd usage in text input as placeholder
self.messageTextField.placeholder = @"Usage: /ban <userId> [<reason>]";
}
} else if ([cmd isEqualToString:kCmdUnbanUser]) {
if (userId) {
// Unban the user
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
[mxHandler.mxRestClient unbanUser:userId inRoom:self.roomId success:^{
} failure:^(NSError *error) {
NSLog(@"Unban user (%@) failed: %@", userId, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
} else {
// Display cmd usage in text input as placeholder
self.messageTextField.placeholder = @"Usage: /unban <userId>";
}
} else if ([cmd isEqualToString:kCmdSetUserPowerLevel]) {
// Retrieve power level
NSString *powerLevel = nil;
while (index < components.count) {
powerLevel = [components objectAtIndex:index++];
if (powerLevel.length) {
// done
break;
}
// reset
powerLevel = nil;
}
// Set power level
if (userId && powerLevel) {
// FIXME
NSLog(@"Set user power level (/op) is not supported yet (%@)", userId);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"/op is not supported yet" message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
} else {
// Display cmd usage in text input as placeholder
self.messageTextField.placeholder = @"Usage: /op <userId> <power level>";
}
} else if ([cmd isEqualToString:kCmdResetUserPowerLevel]) {
if (userId) {
// Reset user power level
// FIXME
NSLog(@"Reset user power level (/deop) is not supported yet (%@)", userId);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"/deop is not supported yet" message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
} else {
// Display cmd usage in text input as placeholder
self.messageTextField.placeholder = @"Usage: /deop <userId>";
}
} else {
NSLog(@"Unrecognised IRC-style command: %@", text);
self.messageTextField.placeholder = [NSString stringWithFormat:@"Unrecognised IRC-style command: %@", cmd];
}
}
return YES;
}
# pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
UIImage *selectedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
if (selectedImage) {
// Create a temporary event to displayed outgoing message (local echo)
NSString *localEventId = [NSString stringWithFormat:@"%@%@", kLocalEchoEventIdPrefix, [[NSProcessInfo processInfo] globallyUniqueString]];
MXEvent *mxEvent = [[MXEvent alloc] init];
mxEvent.roomId = self.roomId;
mxEvent.eventId = localEventId;
mxEvent.eventType = MXEventTypeRoomMessage;
mxEvent.type = kMXEventTypeStringRoomMessage;
// We store temporarily the selected image in cache, use the localId to build temporary url
NSString *dummyURL = [NSString stringWithFormat:@"%@%@", kMediaManagerPrefixForDummyURL, localEventId];
NSData *selectedImageData = UIImageJPEGRepresentation(selectedImage, 0.5);
[MediaManager cachePictureWithData:selectedImageData forURL:dummyURL];
if (tmpCachedAttachments == nil) {
tmpCachedAttachments = [NSMutableArray array];
}
[tmpCachedAttachments addObject:dummyURL];
NSMutableDictionary *thumbnailInfo = [[NSMutableDictionary alloc] init];
[thumbnailInfo setValue:@"image/jpeg" forKey:@"mimetype"];
[thumbnailInfo setValue:[NSNumber numberWithUnsignedInteger:(NSUInteger)selectedImage.size.width] forKey:@"w"];
[thumbnailInfo setValue:[NSNumber numberWithUnsignedInteger:(NSUInteger)selectedImage.size.height] forKey:@"h"];
[thumbnailInfo setValue:[NSNumber numberWithUnsignedInteger:selectedImageData.length] forKey:@"size"];
mxEvent.content = @{@"msgtype":@"m.image", @"thumbnail_info":thumbnailInfo, @"thumbnail_url":dummyURL};
mxEvent.userId = [MatrixHandler sharedHandler].userId;
// Update table sources
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:messages.count inSection:0];
[messages addObject:mxEvent];
// Refresh table display
[self.messagesTableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];
[self scrollToBottomAnimated:YES];
// Upload image and its thumbnail
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
NSUInteger thumbnailSize = ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH - 5 * ROOM_MESSAGE_CELL_IMAGE_MARGIN;
[mxHandler.mxRestClient uploadImage:selectedImage thumbnailSize:thumbnailSize timeout:30 success:^(NSDictionary *imageMessage) {
// Send image
[self postMessage:imageMessage withLocalEventId:localEventId];
} failure:^(NSError *error) {
NSLog(@"Failed to upload image: %@", error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
// Update the temporary event with the failed event id
NSUInteger index = messages.count;
while (index--) {
MXEvent *mxEvent = [messages objectAtIndex:index];
if ([mxEvent.eventId isEqualToString:localEventId]) {
mxEvent.eventId = kFailedEventId;
// Refresh table display
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self scrollToBottomAnimated:YES];
break;
}
}
}];
}
} else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie]) {
//TODO
}
[self dismissMediaPicker];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self dismissMediaPicker];
}
- (void)dismissMediaPicker {
[[AppDelegate theDelegate].masterTabBarController dismissMediaPicker];
}
@end