element-ios/Vector/ViewController/RoomViewController.m

523 lines
18 KiB
Mathematica
Raw Normal View History

/*
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
#import "RoomViewController.h"
2014-10-14 15:56:03 +00:00
#import "AppDelegate.h"
#import "RageShakeManager.h"
2015-08-18 08:04:30 +00:00
#import "RoomInputToolbarView.h"
#import "RoomActivitiesView.h"
2015-11-18 13:42:25 +00:00
#import "RoomTitleViewWithTopic.h"
#import "RoomParticipantsViewController.h"
#import "SegmentedViewController.h"
#import "RoomSettingsViewController.h"
#import "RoomIncomingTextMsgWithPaginationTitleBubbleCell.h"
#import "RoomIncomingAttachmentWithPaginationTitleBubbleCell.h"
#import "RoomOutgoingAttachmentBubbleCell.h"
#import "RoomOutgoingAttachmentHiddenSenderBubbleCell.h"
#import "RoomOutgoingAttachmentWithPaginationTitleBubbleCell.h"
#import "RoomOutgoingTextMsgBubbleCell.h"
#import "RoomOutgoingTextMsgHiddenSenderBubbleCell.h"
#import "RoomOutgoingTextMsgWithPaginationTitleBubbleCell.h"
#import "AvatarGenerator.h"
@interface RoomViewController ()
{
// the user taps on a member thumbnail
MXRoomMember *selectedRoomMember;
// List of members who are typing in the room.
NSArray *currentTypingUsers;
// Typing notifications listener.
id typingNotifListener;
2014-10-14 15:56:03 +00:00
}
2015-08-19 09:05:47 +00:00
@property (strong, nonatomic) MXKAlert *currentAlert;
@end
@implementation RoomViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Register first customized cell view classes used to render bubbles
[self.bubblesTableView registerClass:RoomIncomingTextMsgWithPaginationTitleBubbleCell.class forCellReuseIdentifier:RoomIncomingTextMsgWithPaginationTitleBubbleCell.defaultReuseIdentifier];
[self.bubblesTableView registerClass:RoomIncomingAttachmentWithPaginationTitleBubbleCell.class forCellReuseIdentifier:RoomIncomingAttachmentWithPaginationTitleBubbleCell.defaultReuseIdentifier];
[self.bubblesTableView registerClass:RoomOutgoingAttachmentBubbleCell.class forCellReuseIdentifier:RoomOutgoingAttachmentBubbleCell.defaultReuseIdentifier];
[self.bubblesTableView registerClass:RoomOutgoingAttachmentHiddenSenderBubbleCell.class forCellReuseIdentifier:RoomOutgoingAttachmentHiddenSenderBubbleCell.defaultReuseIdentifier];
[self.bubblesTableView registerClass:RoomOutgoingAttachmentWithPaginationTitleBubbleCell.class forCellReuseIdentifier:RoomOutgoingAttachmentWithPaginationTitleBubbleCell.defaultReuseIdentifier];
[self.bubblesTableView registerClass:RoomOutgoingTextMsgBubbleCell.class forCellReuseIdentifier:RoomOutgoingTextMsgBubbleCell.defaultReuseIdentifier];
[self.bubblesTableView registerClass:RoomOutgoingTextMsgHiddenSenderBubbleCell.class forCellReuseIdentifier:RoomOutgoingTextMsgHiddenSenderBubbleCell.defaultReuseIdentifier];
[self.bubblesTableView registerClass:RoomOutgoingTextMsgWithPaginationTitleBubbleCell.class forCellReuseIdentifier:RoomOutgoingTextMsgWithPaginationTitleBubbleCell.defaultReuseIdentifier];
// Set room title view
[self setRoomTitleViewClass:RoomTitleViewWithTopic.class];
2015-08-18 08:04:30 +00:00
// Replace the default input toolbar view.
// Note: this operation will force the layout of subviews. That is why cell view classes must be registered before.
2015-08-18 08:04:30 +00:00
[self setRoomInputToolbarViewClass:RoomInputToolbarView.class];
[self roomInputToolbarView:self.inputToolbarView heightDidChanged:((RoomInputToolbarView*)self.inputToolbarView).mainToolbarHeightConstraint.constant completion:nil];
2015-11-18 13:42:25 +00:00
// set extra area
[self setRoomActivitiesViewClass:RoomActivitiesView.class];
2015-11-18 13:42:25 +00:00
// Set rageShake handler
self.rageShakeManager = [RageShakeManager sharedManager];
2015-08-18 14:24:48 +00:00
2015-08-19 08:45:45 +00:00
self.navigationItem.rightBarButtonItem.target = self;
self.navigationItem.rightBarButtonItem.action = @selector(onButtonPressed:);
self.navigationItem.rightBarButtonItem.enabled = NO;
2015-08-18 14:24:48 +00:00
2015-11-25 15:53:52 +00:00
// Localize strings here
if (self.roomDataSource)
{
// this room view controller has its own typing management.
self.roomDataSource.showTypingNotifications = NO;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated
{
2014-10-13 16:53:33 +00:00
[super viewWillAppear:animated];
[self listenTypingNotifications];
2014-10-13 16:53:33 +00:00
}
- (void)viewWillDisappear:(BOOL)animated
{
2014-10-13 16:53:33 +00:00
[super viewWillDisappear:animated];
// hide action
2015-08-19 09:05:47 +00:00
if (self.currentAlert)
{
[self.currentAlert dismiss:NO];
self.currentAlert = nil;
}
[self removeTypingNotificationsListener];
2014-10-13 16:53:33 +00:00
}
- (void)viewDidAppear:(BOOL)animated
{
if (self.childViewControllers)
{
// Dispose data source defined for room member list view controller (if any)
for (id childViewController in self.childViewControllers)
{
if ([childViewController isKindOfClass:[MXKRoomMemberListViewController class]])
{
MXKRoomMemberListViewController *viewController = (MXKRoomMemberListViewController*)childViewController;
MXKDataSource *dataSource = [viewController dataSource];
[viewController destroy];
[dataSource destroy];
}
}
}
2014-10-14 15:56:03 +00:00
[super viewDidAppear:animated];
if (self.roomDataSource)
{
// Set visible room id
2015-11-17 23:15:52 +00:00
[AppDelegate theDelegate].visibleRoomId = self.roomDataSource.roomId;
}
2014-10-14 15:56:03 +00:00
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
// Reset visible room id
2015-11-17 23:15:52 +00:00
[AppDelegate theDelegate].visibleRoomId = nil;
}
- (void)viewDidLayoutSubviews
{
UIEdgeInsets contentInset = self.bubblesTableView.contentInset;
contentInset.bottom = self.bottomLayoutGuide.length;
self.bubblesTableView.contentInset = contentInset;
}
#pragma mark - Override MXKRoomViewController
- (void)displayRoom:(MXKRoomDataSource *)dataSource
{
[super displayRoom:dataSource];
self.navigationItem.rightBarButtonItem.enabled = (dataSource != nil);
}
- (void)updateViewControllerAppearanceOnRoomDataSourceState
{
[super updateViewControllerAppearanceOnRoomDataSourceState];
self.navigationItem.rightBarButtonItem.enabled = (self.roomDataSource != nil);
}
- (BOOL)isIRCStyleCommand:(NSString*)string
{
// Override the default behavior for `/join` command in order to open automatically the joined room
if ([string hasPrefix:kCmdJoinRoom])
{
// Join a room
NSString *roomAlias = [string substringFromIndex:kCmdJoinRoom.length + 1];
// Remove white space from both ends
roomAlias = [roomAlias stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
// Check
if (roomAlias.length)
{
[self.mainSession joinRoom:roomAlias success:^(MXRoom *room)
{
// Show the room
2015-11-17 23:15:52 +00:00
[[AppDelegate theDelegate] showRoom:room.state.roomId withMatrixSession:self.mainSession];
} failure:^(NSError *error)
{
NSLog(@"[Console RoomVC] Join roomAlias (%@) failed: %@", roomAlias, error);
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
else
{
// Display cmd usage in text input as placeholder
self.inputToolbarView.placeholder = @"Usage: /join <room_alias>";
}
return YES;
}
return [super isIRCStyleCommand:string];
}
- (void)destroy
{
self.navigationItem.rightBarButtonItem.enabled = NO;
2015-08-19 09:05:47 +00:00
if (self.currentAlert)
{
2015-08-19 09:05:47 +00:00
[self.currentAlert dismiss:NO];
self.currentAlert = nil;
}
[super destroy];
}
#pragma mark - MXKDataSourceDelegate
- (Class<MXKCellRendering>)cellViewClassForCellData:(MXKCellData*)cellData
{
Class cellViewClass = nil;
// Sanity check
if ([cellData conformsToProtocol:@protocol(MXKRoomBubbleCellDataStoring)])
{
id<MXKRoomBubbleCellDataStoring> bubbleData = (id<MXKRoomBubbleCellDataStoring>)cellData;
// Select the suitable table view cell class
if (bubbleData.isIncoming)
{
if (bubbleData.isAttachmentWithThumbnail)
{
if (bubbleData.isPaginationFirstBubble)
{
cellViewClass = RoomIncomingAttachmentWithPaginationTitleBubbleCell.class;
}
else if (bubbleData.shouldHideSenderInformation)
{
cellViewClass = MXKRoomIncomingAttachmentHiddenSenderBubbleCell.class;
}
else
{
cellViewClass = MXKRoomIncomingAttachmentBubbleCell.class;
}
}
else
{
if (bubbleData.isPaginationFirstBubble)
{
cellViewClass = RoomIncomingTextMsgWithPaginationTitleBubbleCell.class;
}
else if (bubbleData.shouldHideSenderInformation)
{
cellViewClass = MXKRoomIncomingTextMsgHiddenSenderBubbleCell.class;
}
else
{
cellViewClass = MXKRoomIncomingTextMsgBubbleCell.class;
}
}
}
else
{
// Handle here outgoing bubbles
if (bubbleData.isAttachmentWithThumbnail)
{
if (bubbleData.isPaginationFirstBubble)
{
cellViewClass = RoomOutgoingAttachmentWithPaginationTitleBubbleCell.class;
}
else if (bubbleData.shouldHideSenderInformation)
{
cellViewClass = RoomOutgoingAttachmentHiddenSenderBubbleCell.class;
}
else
{
cellViewClass = RoomOutgoingAttachmentBubbleCell.class;
}
}
else
{
if (bubbleData.isPaginationFirstBubble)
{
cellViewClass = RoomOutgoingTextMsgWithPaginationTitleBubbleCell.class;
}
else if (bubbleData.shouldHideSenderInformation)
{
cellViewClass = RoomOutgoingTextMsgHiddenSenderBubbleCell.class;
}
else
{
cellViewClass = RoomOutgoingTextMsgBubbleCell.class;
}
}
}
}
return cellViewClass;
}
#pragma mark - MXKDataSource delegate
2014-10-15 23:34:46 +00:00
- (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)actionIdentifier inCell:(id<MXKCellRendering>)cell userInfo:(NSDictionary *)userInfo
{
if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnMessageTextView])
{
self.roomDataSource.showBubblesDateTime = !self.roomDataSource.showBubblesDateTime;
NSLog(@" -> Turn %@ cells date", self.roomDataSource.showBubblesDateTime ? @"ON" : @"OFF");
}
else
{
// Keep default implementation for other actions
[super dataSource:dataSource didRecognizeAction:actionIdentifier inCell:cell userInfo:userInfo];
}
}
2014-12-23 17:51:49 +00:00
#pragma mark - Segues
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Keep ref on destinationViewController
[super prepareForSegue:segue sender:sender];
id pushedViewController = [segue destinationViewController];
2015-11-25 15:53:52 +00:00
if ([[segue identifier] isEqualToString:@"showRoomDetails"])
{
if ([pushedViewController isKindOfClass:[SegmentedViewController class]])
{
// Dismiss keyboard
[self dismissKeyboard];
SegmentedViewController* segmentedViewController = (SegmentedViewController*)pushedViewController;
MXSession* session = self.roomDataSource.mxSession;
NSString* roomid = self.roomDataSource.roomId;
NSMutableArray* viewControllers = [[NSMutableArray alloc] init];
NSMutableArray* titles = [[NSMutableArray alloc] init];
// members screens
[titles addObject: NSLocalizedStringFromTable(@"room_details_people", @"Vector", nil)];
RoomParticipantsViewController* participantsViewController = [[RoomParticipantsViewController alloc] init];
participantsViewController.mxRoom = [session roomWithRoomId:roomid];
[viewControllers addObject:participantsViewController];
[titles addObject: NSLocalizedStringFromTable(@"room_details_settings", @"Vector", nil)];
RoomSettingsViewController *settingsViewController = [RoomSettingsViewController roomSettingsViewController];
[settingsViewController initWithSession:session andRoomId:roomid];
[viewControllers addObject:settingsViewController];
segmentedViewController.title = NSLocalizedStringFromTable(@"room_details_title", @"Vector", nil);
2015-11-26 17:23:50 +00:00
[segmentedViewController initWithTitles:titles viewControllers:viewControllers defaultSelected:0];
// to display a red navbar when the home server cannot be reached.
[segmentedViewController addMatrixSession:session];
}
}
// Hide back button title
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
2014-12-23 17:51:49 +00:00
}
2015-08-18 14:24:48 +00:00
#pragma mark - MXKRoomInputToolbarViewDelegate
- (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView placeCallWithVideo:(BOOL)video
{
[self.mainSession.callManager placeCallInRoom:self.roomDataSource.roomId withVideo:video];
}
#pragma mark - Action
- (IBAction)onButtonPressed:(id)sender
{
2015-08-19 08:45:45 +00:00
if (sender == self.navigationItem.rightBarButtonItem)
{
2015-11-25 15:53:52 +00:00
// FIXME Launch messages search session
}
}
#pragma mark - UITableView delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
}
#pragma mark - RoomDetailsViewController management
- (BOOL)roomTitleViewShouldBeginEditing:(MXKRoomTitleView*)titleView
{
2015-11-25 15:53:52 +00:00
// Instead of editing room title, we open room details view here
dispatch_async(dispatch_get_main_queue(), ^{
2015-11-25 15:53:52 +00:00
[self performSegueWithIdentifier:@"showRoomDetails" sender:self];
2015-11-25 15:53:52 +00:00
});
return NO;
}
#pragma mark - typing management
- (void)removeTypingNotificationsListener
{
if (self.roomDataSource)
{
// Remove the previous live listener
if (typingNotifListener)
{
[self.roomDataSource.room removeListener:typingNotifListener];
currentTypingUsers = nil;
}
}
}
- (void)listenTypingNotifications
{
if (self.roomDataSource)
{
// Add typing notification listener
2015-11-25 15:53:52 +00:00
typingNotifListener = [self.roomDataSource.room listenToEventsOfTypes:@[kMXEventTypeStringTypingNotification] onEvent:^(MXEvent *event, MXEventDirection direction, MXRoomState *roomState) {
// Handle only live events
if (direction == MXEventDirectionForwards)
{
// Retrieve typing users list
NSMutableArray *typingUsers = [NSMutableArray arrayWithArray:self.roomDataSource.room.typingUsers];
// Remove typing info for the current user
NSUInteger index = [typingUsers indexOfObject:self.mainSession.myUser.userId];
if (index != NSNotFound)
{
[typingUsers removeObjectAtIndex:index];
}
// Ignore this notification if both arrays are empty
if (currentTypingUsers.count || typingUsers.count)
{
currentTypingUsers = typingUsers;
[self refreshTypingView];
}
}
}];
currentTypingUsers = self.roomDataSource.room.typingUsers;
[self refreshTypingView];
}
}
- (void)refreshTypingView
{
NSString* text = nil;
NSUInteger count = currentTypingUsers.count;
// get the room member names
NSMutableArray *names = [[NSMutableArray alloc] init];
// keeps the only the first two users
2015-11-25 15:53:52 +00:00
for(int i = 0; i < MIN(count, 2); i++)
{
NSString* name = [currentTypingUsers objectAtIndex:i];
MXRoomMember* member = [self.roomDataSource.room.state memberWithUserId:name];
if (nil != member)
{
name = member.displayname;
}
2015-12-03 09:51:07 +00:00
// sanity check
2015-12-03 10:38:37 +00:00
if (name)
{
[names addObject:name];
}
}
if (0 == count)
{
// something to do ?
}
else if (1 == count)
{
text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_one_user_is_typing", @"Vector", nil), [names objectAtIndex:0]];
}
else if (2 == count)
{
text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_two_users_are_typing", @"Vector", nil), [names objectAtIndex:0], [names objectAtIndex:1]];
}
else
{
text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_many_users_are_typing", @"Vector", nil), [names objectAtIndex:0], [names objectAtIndex:1]];
}
if (self.activitiesView)
{
[((RoomActivitiesView*) self.activitiesView) updateTypingMessage:text];
}
}
@end