Merge remote-tracking branch 'origin/develop' into public_rooms_search

Conflicts:
	Vector/Utils/AvatarGenerator.m
	Vector/ViewController/HomeViewController.m
This commit is contained in:
manuroe
2015-12-23 11:05:46 +01:00
17 changed files with 808 additions and 358 deletions
+3
View File
@@ -631,6 +631,9 @@
{
// Store this new session
[self addMatrixSession:mxSession];
// Each room member will be considered as a potential contact.
[MXKContactManager sharedManager].contactManagerMXRoomSource = MXKContactManagerMXRoomSourceAll;
}
else if (mxSession.state == MXSessionStateStoreDataReady)
{
+8
View File
@@ -85,6 +85,14 @@
"room_participants_admin_name" = "%@ (admin)";
"room_participants_invite_another_user" = "Invite another user";
"room_participants_active" = "Active";
"room_participants_invite" = "Invite";
"room_participants_leave" = "Left";
"room_participants_ban" = "Banned";
"room_participants_active_less_1_hour" = "Active less 1 hour ago";
"room_participants_active_less_x_hours" = "Active less %lu hours ago";
"room_participants_active_less_x_days" = "Active less %lu days ago";
// Chat
"room_one_user_is_typing" = "%@ is typing...";
"room_two_users_are_typing" = "%@ & %@ are typing...";
+3 -3
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9059" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="H1p-Uh-vWS">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="H1p-Uh-vWS">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9049"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--RecentsNav-->
@@ -156,7 +156,7 @@
<scene sceneID="iae-jL-hCr">
<objects>
<tableViewController id="ora-EF-y8t" customClass="RoomCreationStep2ViewController" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" bounces="NO" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="none" rowHeight="44" sectionHeaderHeight="20" sectionFooterHeight="10" id="zDo-5c-N7t">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" bounces="NO" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="zDo-5c-N7t">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+10
View File
@@ -55,4 +55,14 @@
*/
- (void)setRoomAvatarImageIn:(MXKImageView*)mxkImageView;
/*
Observer when a rules deletion fails.
*/
@property id notificationCenterDidFailObserver;
/*
Observer when a rules deletion succeeds.
*/
@property id notificationCenterDidUpdateObserver;
@end
+23 -9
View File
@@ -18,15 +18,7 @@
#import "AvatarGenerator.h"
@interface MXRoom ()
// create property for the extensions
// rule events observer
@property id notificationCenterDidFailObserver;
@property id notificationCenterDidUpdateObserver;
@end
#import <objc/runtime.h>
@implementation MXRoom (Vector)
@@ -370,4 +362,26 @@
return displayName;
}
#pragma mark - observer properties management
- (void)setNotificationCenterDidFailObserver:(id)anObserver
{
objc_setAssociatedObject(self, @selector(notificationCenterDidFailObserver), anObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)notificationCenterDidFailObserver
{
return objc_getAssociatedObject(self, @selector(notificationCenterDidFailObserver));
}
- (void)setNotificationCenterDidUpdateObserver:(id)anObserver
{
objc_setAssociatedObject(self, @selector(notificationCenterDidUpdateObserver), anObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)notificationCenterDidUpdateObserver
{
return objc_getAssociatedObject(self, @selector(notificationCenterDidUpdateObserver));
}
@end
+12 -39
View File
@@ -102,12 +102,19 @@ static UILabel* backgroundLabel = nil;
Returns the UIImage for the text and a selected color.
It checks first if it is not yet cached before generating one.
*/
+ (UIImage*)avatarForText:(NSString*)text andColorIndex:(NSUInteger)colorIndex
+ (UIImage*)avatarForText:(NSString*)aText andColorIndex:(NSUInteger)colorIndex
{
if ([aText hasPrefix:@"@"] || [aText hasPrefix:@"#"])
{
aText = [aText substringFromIndex:1];
}
NSString* firstChar = [[aText substringToIndex:1] uppercaseString];
// the images are cached to avoid create them several times
// the key is <first upper character><index in the colors array>
// it should be smaller than using the text as a key
NSString* key = [NSString stringWithFormat:@"%@%tu", text, colorIndex];
NSString* key = [NSString stringWithFormat:@"%@%tu", firstChar, colorIndex];
if (!imageByKeyDict)
{
@@ -118,7 +125,7 @@ static UILabel* backgroundLabel = nil;
if (!image)
{
image = [AvatarGenerator imageFromText:text withBackgroundColor:[colorsList objectAtIndex:colorIndex]];
image = [AvatarGenerator imageFromText:firstChar withBackgroundColor:[colorsList objectAtIndex:colorIndex]];
[imageByKeyDict setObject:image forKey:key];
}
@@ -127,14 +134,7 @@ static UILabel* backgroundLabel = nil;
+ (UIImage*)generateAvatarForText:(NSString*)text
{
NSUInteger index = [AvatarGenerator colorIndexForText:text];
if (text.length > 0)
{
text = [[text substringToIndex:1] uppercaseString];
}
return [AvatarGenerator avatarForText:text andColorIndex:index];
return [AvatarGenerator avatarForText:text andColorIndex:[AvatarGenerator colorIndexForText:text]];
}
+ (UIImage*)generateRoomMemberAvatar:(NSString*)userId displayName:(NSString*)displayname
@@ -143,39 +143,12 @@ static UILabel* backgroundLabel = nil;
NSUInteger index = [AvatarGenerator colorIndexForText:userId];
NSString* text = displayname ? displayname : userId;
// if the displayname is the userID
// skip the @
if (!displayname && ([text hasPrefix:@"@"] || [text hasPrefix:@"#"]))
{
text = [text substringFromIndex:1];
}
if (text.length > 0)
{
text = [[text substringToIndex:1] uppercaseString];
}
return [AvatarGenerator avatarForText:text andColorIndex:index];
}
+ (UIImage*)generateRoomAvatar:(NSString*)roomId andDisplayName:(NSString*)displayName
{
// the selected color is based on the roomId
NSUInteger index = [AvatarGenerator colorIndexForText:roomId];
NSString* text = displayName;
// ignore the first #
if ([text hasPrefix:@"#"] || [text hasPrefix:@"@"])
{
text = [text substringFromIndex:1];
}
if (text.length > 0)
{
text = [[text substringToIndex:1] uppercaseString];
}
return [AvatarGenerator avatarForText:text andColorIndex:index];
return [AvatarGenerator avatarForText:displayName andColorIndex:[AvatarGenerator colorIndexForText:roomId]];
}
@end
+9
View File
@@ -14,6 +14,13 @@
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
/**
`VectorDesignValues` class manages the Vector design parameters
*/
@interface VectorDesignValues : NSObject
#pragma mark - Vector Colors
#define VECTOR_GREEN_COLOR [UIColor colorWithRed:(98.0/256.0) green:(206.0/256.0) blue:(156.0/256.0) alpha:1.0]
@@ -31,3 +38,5 @@
// to update the navigation bar buttons color
// [[AppDelegate theDelegate] recentsNavigationController].navigationBar.tintColor = [UIColor greenColor];
@end
+4 -3
View File
@@ -332,9 +332,6 @@
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Keep ref on destinationViewController
[super prepareForSegue:segue sender:sender];
if ([[segue identifier] isEqualToString:@"showDetails"])
{
UIViewController *controller;
@@ -390,6 +387,10 @@
{
DirectoryViewController *directoryViewController = segue.destinationViewController;
[directoryViewController displayWitDataSource:recentsDataSource.publicRoomsDirectoryDataSource];
else
{
// Keep ref on destinationViewController
[super prepareForSegue:segue sender:sender];
}
// Hide back button title
+6 -56
View File
@@ -268,57 +268,6 @@
}
#pragma mark - swipe actions
static NSMutableDictionary* backgroundByImageNameDict;
- (UIColor*)getBackgroundColor:(NSString*)imageName
{
UIColor* backgroundColor = VECTOR_LIGHT_GRAY_COLOR;
if (!imageName)
{
return backgroundColor;
}
if (!backgroundByImageNameDict)
{
backgroundByImageNameDict = [[NSMutableDictionary alloc] init];
}
UIColor* bgColor = [backgroundByImageNameDict objectForKey:imageName];
if (!bgColor)
{
CGFloat backgroundSide = 74.0;
CGFloat sourceSide = 30.0;
UIImageView* backgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, backgroundSide, backgroundSide)];
backgroundView.backgroundColor = backgroundColor;
CGFloat offset = (backgroundSide - sourceSide) / 2.0f;
UIImageView* resourceImageView = [[UIImageView alloc] initWithFrame:CGRectMake(offset, offset, sourceSide, sourceSide)];
resourceImageView.backgroundColor = [UIColor clearColor];
resourceImageView.image = [MXKTools resizeImage:[UIImage imageNamed:imageName] toSize:CGSizeMake(sourceSide, sourceSide)];
[backgroundView addSubview:resourceImageView];
// Create a "canvas" (image context) to draw in.
UIGraphicsBeginImageContextWithOptions(backgroundView.frame.size, NO, 0);
// set to the top quality
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
[[backgroundView layer] renderInContext: UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
bgColor = [[UIColor alloc] initWithPatternImage:image];
[backgroundByImageNameDict setObject:bgColor forKey:imageName];
}
return bgColor;
}
// for IOS >= 8 devices
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
@@ -347,7 +296,7 @@ static NSMutableDictionary* backgroundByImageNameDict;
}];
muteAction.backgroundColor = [self getBackgroundColor:isMuted ? @"unmute_icon" : @"mute_icon"];
muteAction.backgroundColor = [MXKTools convertImageToPatternColor:isMuted ? @"unmute_icon" : @"mute_icon" backgroundColor:VECTOR_LIGHT_GRAY_COLOR patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(30, 30)];
[actions insertObject:muteAction atIndex:0];
// favorites management
@@ -376,7 +325,7 @@ static NSMutableDictionary* backgroundByImageNameDict;
[self updateRoomTagAtIndexPath:indexPath to:kMXRoomTagFavourite];
}];
action.backgroundColor = [self getBackgroundColor:@"favorite_icon"];
action.backgroundColor = [MXKTools convertImageToPatternColor:@"favorite_icon" backgroundColor:VECTOR_LIGHT_GRAY_COLOR patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(30, 30)];
[actions insertObject:action atIndex:0];
}
@@ -387,7 +336,7 @@ static NSMutableDictionary* backgroundByImageNameDict;
[self updateRoomTagAtIndexPath:indexPath to:nil];
}];
action.backgroundColor = [self getBackgroundColor:nil];
action.backgroundColor = [MXKTools convertImageToPatternColor:nil backgroundColor:VECTOR_LIGHT_GRAY_COLOR patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(30, 30)];
[actions insertObject:action atIndex:0];
}
@@ -398,14 +347,15 @@ static NSMutableDictionary* backgroundByImageNameDict;
[self updateRoomTagAtIndexPath:indexPath to:kMXRoomTagLowPriority];
}];
action.backgroundColor = [self getBackgroundColor:@"low_priority_icon"];
action.backgroundColor = [MXKTools convertImageToPatternColor:@"low_priority_icon" backgroundColor:VECTOR_LIGHT_GRAY_COLOR patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(30, 30)];
[actions insertObject:action atIndex:0];
}
UITableViewRowAction *leaveAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:title handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
[self leaveRecentsAtIndexPath:indexPath];
}];
leaveAction.backgroundColor = [self getBackgroundColor:@"remove_icon"];
leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon" backgroundColor:VECTOR_LIGHT_GRAY_COLOR patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(30, 30)];
[actions insertObject:leaveAction atIndex:0];
}
@@ -18,6 +18,10 @@
#import "AppDelegate.h"
#import "ContactTableViewCell.h"
#import "VectorDesignValues.h"
@interface RoomCreationStep2ViewController ()
{
UIBarButtonItem *createBarButtonItem;
@@ -102,8 +106,78 @@
super.isAddParticipantSearchBarEditing = isAddParticipantSearchBarEditing;
}
#pragma mark - UITableView delegate
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [[NSMutableArray alloc] init];
}
- (void)customizeContactCell:(ContactTableViewCell*)contactTableViewCell atIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == participantsSection)
{
if (!userMatrixId || (indexPath.row != 0))
{
if (!contactTableViewCell.showCustomAccessoryView)
{
contactTableViewCell.showCustomAccessoryView = YES;
}
if (contactTableViewCell.customAccessoryView.subviews.count == 0)
{
UIImageView* accessView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
accessView.image = [UIImage imageNamed:@"remove_icon"];
accessView.tag = indexPath.row;
accessView.userInteractionEnabled = YES;
UITapGestureRecognizer * accessViewTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onDeleteTap:)];
[accessViewTapGesture setNumberOfTouchesRequired:1];
[accessViewTapGesture setNumberOfTapsRequired:1];
[accessView addGestureRecognizer:accessViewTapGesture];
// add the cross image once.
[contactTableViewCell.customAccessoryView addSubview:accessView];
}
else
{
// update the tag because it provides the row to delete
UIImageView* accessView = [contactTableViewCell.customAccessoryView.subviews objectAtIndex:0];
accessView.tag = indexPath.row;
}
}
else
{
contactTableViewCell.showCustomAccessoryView = NO;
}
contactTableViewCell.selectionStyle = UITableViewCellSelectionStyleNone;
}
else
{
contactTableViewCell.showCustomAccessoryView = NO;
contactTableViewCell.selectionStyle = UITableViewCellSelectionStyleDefault;
}
}
#pragma mark - Actions
- (void)onDeleteTap:(UIGestureRecognizer*)gestureRecognizer
{
NSInteger row = gestureRecognizer.view.tag;
if (userMatrixId)
{
row --;
}
if (row < mutableParticipants.count)
{
[mutableParticipants removeObjectAtIndex:row];
[self.tableView reloadData];
}
}
- (IBAction)onButtonPressed:(id)sender
{
if (sender == createBarButtonItem && _roomCreationInputs.mxSession)
@@ -16,6 +16,8 @@
#import <MatrixKit/MatrixKit.h>
#import "ContactTableViewCell.h"
/**
'RoomParticipantsViewController' instance is used to edit members of the room defined by the property 'mxRoom'.
@@ -32,7 +34,6 @@
/**
Section indexes
*/
NSInteger addParticipantsSection;
NSInteger searchResultSection;
NSInteger participantsSection;
@@ -57,5 +58,14 @@
*/
@property (nonatomic) BOOL isAddParticipantSearchBarEditing;
/**
Customize the UITableViewCell before rendering it.
@param contactCell the cell to customize.
@param indexPath path of the cell in the tableview.
*/
- (void)customizeContactCell:(ContactTableViewCell*)contactCell atIndexPath:(NSIndexPath *)indexPath;
@end
@@ -30,6 +30,8 @@
MXKTableViewCellWithSearchBar *addParticipantsSearchBarCell;
NSString *addParticipantsSearchText;
UIView* searchBarSeparator;
// Search result section
NSMutableArray *filteredParticipants;
@@ -43,9 +45,6 @@
// Observe kMXSessionWillLeaveRoomNotification to be notified if the user leaves the current room.
id leaveRoomNotificationObserver;
// Internal measurement
CGFloat actionButtonWidth;
}
@end
@@ -81,8 +80,8 @@
}
addParticipantsSearchBarCell = [[MXKTableViewCellWithSearchBar alloc] init];
addParticipantsSearchBarCell.contentView.backgroundColor = [UIColor whiteColor];
addParticipantsSearchBarCell.mxkSearchBar.searchBarStyle = UISearchBarStyleMinimal;
// addParticipantsSearchBarCell.mxkSearchBar.barTintColor = [UIColor whiteColor]; // set barTint in case of UISearchBarStyleDefault (= UISearchBarStyleProminent)
addParticipantsSearchBarCell.mxkSearchBar.returnKeyType = UIReturnKeyDone;
addParticipantsSearchBarCell.mxkSearchBar.delegate = self;
addParticipantsSearchBarCell.mxkSearchBar.placeholder = NSLocalizedStringFromTable(@"room_participants_invite_another_user", @"Vector", nil);
@@ -99,26 +98,10 @@
{
mxkContactsById = [NSMutableDictionary dictionary];
}
// Measure the minimum width of the action button displayed in participant cells
MXKContactTableCell *tmpCell = [[MXKContactTableCell alloc] init];
UIButton *actionButton = tmpCell.contactAccessoryButton;
[actionButton setTitle:NSLocalizedStringFromTable(@"leave", @"Vector", nil) forState:UIControlStateNormal];
[actionButton setTitleColor:VECTOR_GREEN_COLOR forState:UIControlStateNormal];
[actionButton sizeToFit];
actionButtonWidth = actionButton.frame.size.width;
[actionButton setTitle:NSLocalizedStringFromTable(@"remove", @"Vector", nil) forState:UIControlStateNormal];
[actionButton setTitleColor:VECTOR_GREEN_COLOR forState:UIControlStateNormal];
[actionButton sizeToFit];
if (actionButton.frame.size.width > actionButtonWidth)
{
actionButtonWidth = actionButton.frame.size.width;
}
// ensure that the separator line is not displayed
self.tableView.separatorColor = [UIColor clearColor];
self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
[self setNavBarButtons];
}
@@ -294,8 +277,7 @@
_isAddParticipantSearchBarEditing = isAddParticipantsSearchBarEditing;
// Switch the display between search result and participants list
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange (1, 1)];
[self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationNone];
[self.tableView reloadData];
}
}
@@ -453,17 +435,15 @@
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSInteger count = 0;
addParticipantsSection = searchResultSection = participantsSection = -1;
searchResultSection = participantsSection = -1;
if (_isAddParticipantSearchBarEditing)
{
// Only "add participant" section is displayed
addParticipantsSection = count++;
searchResultSection = count++;
}
else
{
addParticipantsSection = count++;
participantsSection = count++;
}
@@ -474,11 +454,7 @@
{
NSInteger count = 0;
if (section == addParticipantsSection)
{
count = 1;
}
else if (section == searchResultSection)
if (section == searchResultSection)
{
count = filteredParticipants.count;
}
@@ -494,116 +470,45 @@
return count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
- (void)customizeContactCell:(ContactTableViewCell*)contactCell atIndexPath:(NSIndexPath*) indexPath
{
if (section == participantsSection)
{
NSInteger count = mutableParticipants.count;
if (userMatrixId)
{
count++;
}
if (count > 1)
{
return [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_participants_multi_participants", @"Vector", nil), count];
}
else
{
return NSLocalizedStringFromTable(@"room_participants_one_participant", @"Vector", nil);
}
}
return nil;
// TODO by the inherited class
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
if (indexPath.section == addParticipantsSection)
if ((indexPath.section == searchResultSection) || (indexPath.section == participantsSection))
{
if (indexPath.row == 0)
{
cell = addParticipantsSearchBarCell;
if (_isAddParticipantSearchBarEditing)
{
[addParticipantsSearchBarCell.mxkSearchBar becomeFirstResponder];
}
}
}
else if (indexPath.section == searchResultSection)
{
if (indexPath.row < filteredParticipants.count)
{
MXKContactTableCell* filteredParticipantCell = [tableView dequeueReusableCellWithIdentifier:[MXKContactTableCell defaultReuseIdentifier]];
if (!filteredParticipantCell)
{
filteredParticipantCell = [[MXKContactTableCell alloc] init];
filteredParticipantCell.thumbnailDisplayBoxType = MXKContactTableCellThumbnailDisplayBoxTypeCircle;
filteredParticipantCell.hideMatrixPresence = YES;
}
[filteredParticipantCell render:filteredParticipants[indexPath.row]];
// Show 'add' icon.
filteredParticipantCell.contactAccessoryViewType = MXKContactTableCellAccessoryCustom;
filteredParticipantCell.contactAccessoryViewHeightConstraint.constant = 30;
filteredParticipantCell.contactAccessoryViewWidthConstraint.constant = 30;
filteredParticipantCell.contactAccessoryImageView.image = [UIImage imageNamed:@"add"];
filteredParticipantCell.contactAccessoryImageView.hidden = NO;
filteredParticipantCell.contactAccessoryView.hidden = NO;
filteredParticipantCell.selectionStyle = UITableViewCellSelectionStyleNone;
cell = filteredParticipantCell;
}
}
else if (indexPath.section == participantsSection)
{
MXKContactTableCell *participantCell = [tableView dequeueReusableCellWithIdentifier:[MXKContactTableCell defaultReuseIdentifier]];
ContactTableViewCell* participantCell = [tableView dequeueReusableCellWithIdentifier:[ContactTableViewCell defaultReuseIdentifier]];
if (!participantCell)
{
participantCell = [[MXKContactTableCell alloc] init];
participantCell.thumbnailDisplayBoxType = MXKContactTableCellThumbnailDisplayBoxTypeCircle;
participantCell.hideMatrixPresence = YES;
participantCell = [[ContactTableViewCell alloc] init];
// do not show the custom accessory view
participantCell.showCustomAccessoryView = NO;
}
participantCell.mxRoom = self.mxRoom;
if (userMatrixId && indexPath.row == 0)
Contact *contact = nil;
// oneself dedicated cell
if ((indexPath.section == participantsSection && userMatrixId && indexPath.row == 0))
{
Contact *contact = [mxkContactsById objectForKey:userMatrixId];
if (! contact)
contact = [mxkContactsById objectForKey:userMatrixId];
if (!contact)
{
contact = [[Contact alloc] initMatrixContactWithDisplayName:NSLocalizedStringFromTable(@"you", @"Vector", nil) andMatrixID:userMatrixId];
contact.mxMember = [self.mxRoom.state memberWithUserId:userMatrixId];
[mxkContactsById setObject:contact forKey:userMatrixId];
}
[participantCell render:contact];
if (self.mxRoom)
{
// Show 'Leave' buton.
participantCell.contactAccessoryViewType = MXKContactTableCellAccessoryCustom;
UIButton *actionButton = participantCell.contactAccessoryButton;
actionButton.hidden = NO;
[actionButton setTitle:NSLocalizedStringFromTable(@"leave", @"Vector", nil) forState:UIControlStateNormal];
[actionButton setTitleColor:VECTOR_GREEN_COLOR forState:UIControlStateNormal];
[actionButton addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
actionButton.tag = 0;
[actionButton sizeToFit];
participantCell.contactAccessoryViewHeightConstraint.constant = actionButton.frame.size.height;
participantCell.contactAccessoryViewWidthConstraint.constant = actionButtonWidth;
[participantCell needsUpdateConstraints];
participantCell.contactAccessoryView.hidden = NO;
}
else
{
// Hide accessory view.
participantCell.contactAccessoryViewType = MXKContactTableCellAccessoryCustom;
}
participantCell.selectionStyle = UITableViewCellSelectionStyleNone;
}
else if (indexPath.section == searchResultSection)
{
contact = filteredParticipants[indexPath.row];
}
else
{
@@ -617,7 +522,8 @@
if (index < mutableParticipants.count)
{
NSString *userId = mutableParticipants[index];
Contact *contact = [mxkContactsById objectForKey:userId];
contact = [mxkContactsById objectForKey:userId];
if (!contact)
{
// Create this missing contact
@@ -641,43 +547,31 @@
}
}
if (contact)
{
[participantCell render:contact];
}
if (self.mxRoom)
{
// Show 'remove' button.
participantCell.contactAccessoryViewType = MXKContactTableCellAccessoryCustom;
UIButton *actionButton = participantCell.contactAccessoryButton;
actionButton.hidden = NO;
[actionButton setTitle:NSLocalizedStringFromTable(@"remove", @"Vector", nil) forState:UIControlStateNormal];
[actionButton setTitleColor:VECTOR_GREEN_COLOR forState:UIControlStateNormal];
[actionButton addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
actionButton.tag = indexPath.row;
[actionButton sizeToFit];
participantCell.contactAccessoryViewHeightConstraint.constant = actionButton.frame.size.height;
participantCell.contactAccessoryViewWidthConstraint.constant = actionButtonWidth;
participantCell.contactAccessoryView.hidden = NO;
}
else
{
// Show 'remove' icon.
participantCell.contactAccessoryViewType = MXKContactTableCellAccessoryCustom;
participantCell.contactAccessoryViewHeightConstraint.constant = 30;
participantCell.contactAccessoryViewWidthConstraint.constant = 30;
participantCell.contactAccessoryImageView.image = [UIImage imageNamed:@"remove"];
participantCell.contactAccessoryImageView.hidden = NO;
participantCell.contactAccessoryView.hidden = NO;
}
participantCell.selectionStyle = UITableViewCellSelectionStyleNone;
}
}
if (indexPath.section == searchResultSection)
{
participantCell.selectionStyle = UITableViewCellSelectionStyleDefault;
participantCell.bottomLineSeparator.hidden = ((indexPath.row+1) != filteredParticipants.count);
}
else
{
participantCell.selectionStyle = UITableViewCellSelectionStyleNone;
if (userMatrixId)
{
participantCell.bottomLineSeparator.hidden = ((indexPath.row) != mutableParticipants.count);
}
else
{
participantCell.bottomLineSeparator.hidden = ((indexPath.row+1) != mutableParticipants.count);
}
}
[self customizeContactCell:participantCell atIndexPath:indexPath];
[participantCell render:contact];
cell = participantCell;
}
@@ -686,55 +580,30 @@
#pragma mark - UITableView delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (indexPath.section == addParticipantsSection && indexPath.row == 1)
{
return 10;
}
return 44;
return addParticipantsSearchBarCell.contentView.frame.size.height;
}
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if([view isKindOfClass:[UITableViewHeaderFooterView class]])
{
UITableViewHeaderFooterView *tableViewHeaderFooterView = (UITableViewHeaderFooterView *) view;
tableViewHeaderFooterView.textLabel.text = [tableViewHeaderFooterView.textLabel.text capitalizedString];
tableViewHeaderFooterView.textLabel.font = [UIFont boldSystemFontOfSize:17];
tableViewHeaderFooterView.textLabel.textColor = VECTOR_GREEN_COLOR;
}
return addParticipantsSearchBarCell.contentView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 74.0;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger index = indexPath.row;
NSInteger row = indexPath.row;
if ((indexPath.section == participantsSection) && (self.mxRoom == nil))
if (indexPath.section == searchResultSection)
{
if (userMatrixId)
if (row < filteredParticipants.count)
{
index --;
}
if (index < mutableParticipants.count)
{
[mxkContactsById removeObjectForKey:mutableParticipants[index]];
[mutableParticipants removeObjectAtIndex:index];
// Refresh display
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
UITableViewHeaderFooterView *participantsSectionHeader = [tableView headerViewForSection:participantsSection];
participantsSectionHeader.textLabel.text = [self tableView:tableView titleForHeaderInSection:participantsSection];
}
}
else if (indexPath.section == searchResultSection)
{
if (index < filteredParticipants.count)
{
MXKContact *contact = filteredParticipants[index];
MXKContact *contact = filteredParticipants[row];
NSArray *identifiers = contact.matrixIdentifiers;
if (identifiers.count)
@@ -749,7 +618,7 @@
{
[self addPendingActionMask];
[self.mxRoom inviteUser:participantId success:^{
[self removePendingActionMask];
// Refresh display by leaving search session
@@ -762,7 +631,6 @@
NSLog(@"[RoomParticipantsVC] Invite %@ failed: %@", participantId, error);
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
else
@@ -773,20 +641,44 @@
[self searchBarCancelButtonClicked:addParticipantsSearchBarCell.mxkSearchBar];
}
}
}
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray* actions = [[NSMutableArray alloc] init];
// add the swipe to delete on search and participants section
if (indexPath.section == participantsSection)
{
NSString* title = @" ";
UITableViewRowAction *leaveAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:title handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
[self onDeleteAt:indexPath];
}];
leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon" backgroundColor:VECTOR_LIGHT_GRAY_COLOR patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(30, 30)];
[actions insertObject:leaveAction atIndex:0];
}
return actions;
}
#pragma mark - Actions
- (IBAction)onButtonPressed:(id)sender
- (void)onDeleteAt:(NSIndexPath*)path
{
if ([sender isKindOfClass:[UIButton class]])
NSUInteger section = path.section;
NSUInteger row = path.row;
if (section == participantsSection)
{
UIButton *actionButton = (UIButton*)sender;
NSInteger index = actionButton.tag;
__weak typeof(self) weakSelf = self;
if (currentAlert)
@@ -795,7 +687,7 @@
currentAlert = nil;
}
if (userMatrixId && index == 0 && [actionButton.titleLabel.text isEqualToString:NSLocalizedStringFromTable(@"leave", @"Vector", nil)])
if (userMatrixId && (0 == row))
{
// Leave ?
currentAlert = [[MXKAlert alloc] initWithTitle:NSLocalizedStringFromTable(@"room_participants_leave_prompt_title", @"Vector", nil)
@@ -821,7 +713,15 @@
[strongSelf addPendingActionMask];
[strongSelf.mxRoom leave:^{
[strongSelf withdrawViewControllerAnimated:YES completion:nil];
// check if there is a parent view controller
if (strongSelf.parentViewController)
{
[strongSelf.navigationController popViewControllerAnimated:YES];
}
else
{
[strongSelf withdrawViewControllerAnimated:YES completion:nil];
}
} failure:^(NSError *error) {
@@ -836,16 +736,16 @@
[currentAlert showInViewController:self];
}
else if ([actionButton.titleLabel.text isEqualToString:NSLocalizedStringFromTable(@"remove", @"Vector", nil)])
else
{
if (userMatrixId)
{
index --;
row --;
}
if (index < mutableParticipants.count)
if (row < mutableParticipants.count)
{
NSString *memberUserId = mutableParticipants[index];
NSString *memberUserId = mutableParticipants[row];
MXKContact *contact = [mxkContactsById objectForKey:memberUserId];
// Kick ?
@@ -872,25 +772,25 @@
[strongSelf addPendingActionMask];
[strongSelf.mxRoom kickUser:memberUserId
reason:nil
success:^{
[strongSelf removePendingActionMask];
[strongSelf->mxkContactsById removeObjectForKey:memberUserId];
[strongSelf->mutableParticipants removeObjectAtIndex:index];
// Refresh display
[strongSelf.tableView reloadData];
} failure:^(NSError *error) {
[strongSelf removePendingActionMask];
NSLog(@"[RoomParticipantsVC] Kick %@ failed: %@", memberUserId, error);
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
reason:nil
success:^{
[strongSelf removePendingActionMask];
[strongSelf->mxkContactsById removeObjectForKey:memberUserId];
[strongSelf->mutableParticipants removeObjectAtIndex:row];
// Refresh display
[strongSelf.tableView reloadData];
} failure:^(NSError *error) {
[strongSelf removePendingActionMask];
NSLog(@"[RoomParticipantsVC] Kick %@ failed: %@", memberUserId, error);
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}];
[currentAlert showInViewController:self];
@@ -909,36 +809,85 @@
// text color
UITextField *searchBarTextField = [searchBar valueForKey:@"_searchField"];
searchBarTextField.textColor = VECTOR_GREEN_COLOR;
searchBarTextField.textColor = VECTOR_TEXT_GRAY_COLOR;
// Magnifying glass icon.
UIImageView *leftImageView = (UIImageView *)searchBarTextField.leftView;
leftImageView.image = [leftImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
leftImageView.tintColor = VECTOR_GREEN_COLOR;
// Clear button
UIButton *clearButton = [searchBarTextField valueForKey:@"_clearButton"];
[clearButton setImage:[clearButton.imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
clearButton.tintColor = VECTOR_GREEN_COLOR;
// remove the gray background color
UIView *effectBackgroundTop = [searchBarTextField valueForKey:@"_effectBackgroundTop"];
UIView *effectBackgroundBottom = [searchBarTextField valueForKey:@"_effectBackgroundBottom"];
effectBackgroundTop.hidden = YES;
effectBackgroundBottom.hidden = YES;
// add line separator under the textfield
if (!searchBarSeparator)
{
searchBarSeparator = [[UIView alloc] init];
searchBarSeparator.backgroundColor = VECTOR_GREEN_COLOR;
[searchBarTextField addSubview:searchBarSeparator];
searchBarSeparator.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint* leftConstraint = [NSLayoutConstraint constraintWithItem:searchBarSeparator
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:searchBarTextField
attribute:NSLayoutAttributeLeading
multiplier:1
constant:0];
NSLayoutConstraint* widthConstraint = [NSLayoutConstraint constraintWithItem:searchBarSeparator
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:searchBarTextField
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:searchBarSeparator
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:1];
NSLayoutConstraint* bottomConstraint = [NSLayoutConstraint constraintWithItem:searchBarSeparator
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:searchBarTextField
attribute:NSLayoutAttributeBottom
multiplier:1
constant:0];
[NSLayoutConstraint activateConstraints:@[leftConstraint, widthConstraint, heightConstraint, bottomConstraint]];
}
// place holder
[searchBarTextField setValue:VECTOR_GREEN_COLOR forKeyPath:@"_placeholderLabel.textColor"];
[searchBarTextField setValue:VECTOR_TEXT_GRAY_COLOR forKeyPath:@"_placeholderLabel.textColor"];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
NSInteger previousFilteredCount = filteredParticipants.count;
NSMutableArray *mxUsers;
NSMutableArray *constacts;
if (addParticipantsSearchText.length && [searchText hasPrefix:addParticipantsSearchText])
{
mxUsers = filteredParticipants;
constacts = filteredParticipants;
}
else
{
// Retrieve all known matrix users
NSArray *matrixContacts = [NSMutableArray arrayWithArray:[MXKContactManager sharedManager].matrixContacts];
mxUsers = [NSMutableArray arrayWithCapacity:matrixContacts.count];
constacts = [NSMutableArray arrayWithCapacity:matrixContacts.count];
// Split contacts with several ids, and remove the current participants.
for (MXKContact* contact in matrixContacts)
@@ -954,7 +903,7 @@
{
Contact *splitContact = [[Contact alloc] initMatrixContactWithDisplayName:contact.displayName andMatrixID:userId];
splitContact.mxMember = [self.mxRoom.state memberWithUserId:userId];
[mxUsers addObject:splitContact];
[constacts addObject:splitContact];
}
}
}
@@ -966,7 +915,7 @@
{
if (![userId isEqualToString:userMatrixId])
{
[mxUsers addObject:contact];
[constacts addObject:contact];
}
}
}
@@ -978,7 +927,7 @@
NSMutableArray *indexArray = [NSMutableArray array];
NSInteger index = 0;
for (MXKContact* contact in mxUsers)
for (MXKContact* contact in constacts)
{
if ([contact matchedWithPatterns:@[addParticipantsSearchText]])
{
@@ -987,9 +936,9 @@
}
}
if (previousFilteredCount || filteredParticipants.count)
if ((searchResultSection != -1) && (previousFilteredCount || filteredParticipants.count))
{
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange (searchResultSection, 1)];
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(searchResultSection, 1)];
[self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationNone];
}
}
+1 -1
View File
@@ -880,7 +880,7 @@
MXRoomMember* member = [self.roomDataSource.room.state memberWithUserId:name];
if (nil != member)
if (member && member.displayname.length)
{
name = member.displayname;
}
@@ -0,0 +1,46 @@
/*
Copyright 2015 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 <MatrixSDK/MatrixSDK.h>
#import "MXKTableViewCell.h"
#import "MXKCellRendering.h"
#import "MXKImageView.h"
/**
'ContactTableCell' extends MXKTableViewCell.
*/
@interface ContactTableViewCell : MXKTableViewCell <MXKCellRendering>
@property (strong, nonatomic) IBOutlet MXKImageView *thumbnailView;
@property (strong, nonatomic) IBOutlet UILabel *contactDisplayNameLabel;
@property (weak, nonatomic) IBOutlet UILabel *lastPresenceLabel;
@property (weak, nonatomic) IBOutlet UIView *bottomLineSeparator;
@property (weak, nonatomic) IBOutlet UIView *topLineSeparator;
@property (weak, nonatomic) IBOutlet UIView *customAccessoryView;
@property (nonatomic) BOOL showCustomAccessoryView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *customAccessViewWidthConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *customAccessoryViewLeadingConstraint;
// The room where the contact is.
// It is used to display the member information (like invitation)
// This property is OPTIONAL.
@property (nonatomic) MXRoom* mxRoom;
@end
+285
View File
@@ -0,0 +1,285 @@
/*
Copyright 2015 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 "ContactTableViewCell.h"
#import "MXKContactManager.h"
#import "VectorDesignValues.h"
#import "AvatarGenerator.h"
#import "MXKContactManager.h"
@interface ContactTableViewCell()
{
// The current displayed contact.
MXKContact *contact;
/**
The observer of the presence for matrix user.
*/
id mxPresenceObserver;
}
@end
@implementation ContactTableViewCell
@synthesize mxRoom;
- (void)awakeFromNib
{
[super awakeFromNib];
self.thumbnailView.layer.cornerRadius = self.thumbnailView.frame.size.width / 2;
self.thumbnailView.clipsToBounds = YES;
// apply the vector colours
self.bottomLineSeparator.backgroundColor = VECTOR_SILVER_COLOR;
self.topLineSeparator.backgroundColor = VECTOR_SILVER_COLOR;
self.lastPresenceLabel.textColor = VECTOR_TEXT_GRAY_COLOR;
}
- (void)setShowCustomAccessoryView:(BOOL)show
{
_showCustomAccessoryView = show;
if (show)
{
self.customAccessViewWidthConstraint.constant = 25;
self.customAccessoryViewLeadingConstraint.constant = 13;
}
else
{
self.customAccessViewWidthConstraint.constant = 0;
self.customAccessoryViewLeadingConstraint.constant = 0;
}
}
#pragma mark - MXKCellRendering
// returns the first matrix id of the contact
// nil if there is none
- (NSString*)getFirstMatrixId
{
NSString* matrixId = nil;
if (contact.matrixIdentifiers.count > 0)
{
matrixId = contact.matrixIdentifiers.firstObject;
}
return matrixId;
}
- (void)render:(MXKCellData *)cellData
{
// remove any pending observers
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (mxPresenceObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:mxPresenceObserver];
mxPresenceObserver = nil;
}
// Sanity check: accept only object of MXKContact classes or sub-classes
NSParameterAssert([cellData isKindOfClass:[MXKContact class]]);
contact = (MXKContact*)cellData;
// sanity check
// should never happen
if (!contact)
{
self.thumbnailView.image = nil;
self.contactDisplayNameLabel.text = nil;
self.lastPresenceLabel.text = nil;
return;
}
// Be warned when the thumbnail is updated
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onThumbnailUpdate:) name:kMXKContactThumbnailUpdateNotification object:nil];
// Observe contact presence change
mxPresenceObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKContactManagerMatrixUserPresenceChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
NSString* matrixId = [self getFirstMatrixId];
if (matrixId && [matrixId isEqualToString:notif.object])
{
[self refreshContactPresence];
}
}];
if (!contact.isMatrixContact)
{
// Refresh matrix info of the contact
[[MXKContactManager sharedManager] updateMatrixIDsForLocalContact:contact];
}
[self refreshContactDisplayName];
[self refreshContactPresence];
[self refreshContactThumbnail];
}
+ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth
{
return 74;
}
- (void)didEndDisplay
{
// remove any pending observers
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (mxPresenceObserver) {
[[NSNotificationCenter defaultCenter] removeObserver:mxPresenceObserver];
mxPresenceObserver = nil;
}
// Remove all gesture recognizer
while (self.thumbnailView.gestureRecognizers.count)
{
[self.thumbnailView removeGestureRecognizer:self.thumbnailView.gestureRecognizers[0]];
}
self.delegate = nil;
contact = nil;
}
#pragma mark Refresh cell part
- (void)refreshContactThumbnail
{
UIImage* image = [contact thumbnailWithPreferedSize:self.thumbnailView.frame.size];
if (!image)
{
NSString* matrixId = [self getFirstMatrixId];
if (matrixId)
{
image = [AvatarGenerator generateRoomMemberAvatar:matrixId displayName:contact.displayName];
}
else
{
image = [AvatarGenerator generateAvatarForText:contact.displayName];
}
}
self.thumbnailView.image = image;
}
- (void)refreshContactDisplayName
{
self.contactDisplayNameLabel.text = contact.displayName;
}
- (void)refreshContactPresence
{
NSString* presenceText = nil;
NSString* matrixId = [self getFirstMatrixId];
MXRoomMember* member = nil;
if (self.mxRoom)
{
member = [self.mxRoom.state memberWithUserId:matrixId];
}
if (!member || (member.membership != MXMembershipJoin))
{
if (member.membership == MXMembershipInvite)
{
presenceText = NSLocalizedStringFromTable(@"room_participants_invite", @"Vector", nil);
}
else if (member.membership == MXMembershipLeave)
{
presenceText = NSLocalizedStringFromTable(@"room_participants_leave", @"Vector", nil);
}
else if (member.membership == MXMembershipBan)
{
presenceText = NSLocalizedStringFromTable(@"room_participants_ban", @"Vector", nil);
}
}
else
{
MXUser *user = nil;
// Consider here all sessions reported into contact manager
NSArray* mxSessions = [MXKContactManager sharedManager].mxSessions;
for (MXSession *mxSession in mxSessions)
{
user = [mxSession userWithUserId:matrixId];
if (user)
{
break;
}
}
if (user)
{
if (user.presence == MXPresenceOnline)
{
presenceText = NSLocalizedStringFromTable(@"room_participants_active", @"Vector", nil);
}
else
{
NSUInteger lastActiveMs = user.lastActiveAgo;
if (-1 != lastActiveMs)
{
NSUInteger lastActivehour = lastActiveMs / 1000 / 60 / 60;
NSUInteger lastActiveDays = lastActivehour / 24;
if (lastActivehour < 1)
{
presenceText = NSLocalizedStringFromTable(@"room_participants_active_less_1_hour", @"Vector", nil);
}
else if (lastActivehour < 24)
{
presenceText = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_participants_active_less_x_hours", @"Vector", nil), lastActivehour];
}
else
{
presenceText = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_participants_active_less_x_days", @"Vector", nil), lastActiveDays];
}
}
}
}
}
self.lastPresenceLabel.text = presenceText;
}
#pragma mark - events
- (void)onThumbnailUpdate:(NSNotification *)notif
{
// sanity check
if ([notif.object isKindOfClass:[NSString class]])
{
NSString* contactID = notif.object;
if ([contactID isEqualToString:contact.contactID])
{
[self refreshContactThumbnail];
}
}
}
@end
@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="L2L-l5-wPx" customClass="ContactTableViewCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="74"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="L2L-l5-wPx" id="aXz-IR-jj5">
<rect key="frame" x="0.0" y="0.0" width="600" height="73"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KGN-cP-igr" userLabel="bottom separator">
<rect key="frame" x="13" y="73" width="587" height="1"/>
<color key="backgroundColor" red="1" green="0.42877345709999998" blue="0.42329320510000001" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="lUd-8E-WAj"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="AqX-al-ZBq" userLabel="top separator">
<rect key="frame" x="13" y="0.0" width="587" height="1"/>
<color key="backgroundColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="hXb-Ib-cjB"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RX5-eD-c3c" userLabel="member avatar" customClass="MXKImageView">
<rect key="frame" x="13" y="15" width="42" height="42"/>
<color key="backgroundColor" white="0.89720269880000003" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="42" id="7Ys-gS-ja8"/>
<constraint firstAttribute="height" constant="42" id="WPC-tL-hnM"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="Display name" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Lg1-xQ-AGn" userLabel="member display name">
<rect key="frame" x="69" y="14" width="481" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="vCd-fx-d5z"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="last presence" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dQt-mN-T6b" userLabel="Last presence">
<rect key="frame" x="69" y="39" width="531" height="20"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="e6p-DU-3ny"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ogo-Qt-u2C" userLabel="Custom accessory view">
<rect key="frame" x="563" y="25" width="24" height="24"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="pDU-SS-0mb"/>
<constraint firstAttribute="height" constant="24" id="w2M-nj-D56"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="Lg1-xQ-AGn" firstAttribute="leading" secondItem="RX5-eD-c3c" secondAttribute="trailing" constant="14" id="Pgp-JM-oQd"/>
<constraint firstItem="dQt-mN-T6b" firstAttribute="leading" secondItem="RX5-eD-c3c" secondAttribute="trailing" constant="14" id="XFM-LG-4uJ"/>
<constraint firstItem="dQt-mN-T6b" firstAttribute="top" secondItem="Lg1-xQ-AGn" secondAttribute="bottom" constant="4" id="iaT-57-GOs"/>
<constraint firstItem="RX5-eD-c3c" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="top" constant="15" id="mga-fG-I0L"/>
<constraint firstItem="Ogo-Qt-u2C" firstAttribute="leading" secondItem="Lg1-xQ-AGn" secondAttribute="trailing" constant="13" id="oNi-JM-zCJ"/>
<constraint firstAttribute="trailing" secondItem="dQt-mN-T6b" secondAttribute="trailing" id="t2m-pb-5zd"/>
<constraint firstItem="Lg1-xQ-AGn" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="top" constant="14" id="tY3-6V-A3B"/>
<constraint firstItem="RX5-eD-c3c" firstAttribute="leading" secondItem="aXz-IR-jj5" secondAttribute="leading" constant="13" id="tgy-cX-Wxm"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="KGN-cP-igr" firstAttribute="leading" secondItem="L2L-l5-wPx" secondAttribute="leading" constant="13" id="5Ts-MV-2Oe"/>
<constraint firstItem="AqX-al-ZBq" firstAttribute="top" secondItem="L2L-l5-wPx" secondAttribute="top" id="84t-i4-Nm1"/>
<constraint firstAttribute="trailing" secondItem="AqX-al-ZBq" secondAttribute="trailing" id="B9V-Y3-xKb"/>
<constraint firstItem="Ogo-Qt-u2C" firstAttribute="centerY" secondItem="L2L-l5-wPx" secondAttribute="centerY" id="LYX-FJ-cz3"/>
<constraint firstAttribute="trailing" secondItem="KGN-cP-igr" secondAttribute="trailing" id="P2b-NY-hCQ"/>
<constraint firstAttribute="trailing" secondItem="Ogo-Qt-u2C" secondAttribute="trailing" constant="13" id="cWl-2f-cvI"/>
<constraint firstAttribute="bottom" secondItem="KGN-cP-igr" secondAttribute="bottom" id="uIP-Xo-qav"/>
<constraint firstItem="AqX-al-ZBq" firstAttribute="leading" secondItem="L2L-l5-wPx" secondAttribute="leading" constant="13" id="wIj-F8-G1Q"/>
</constraints>
<connections>
<outlet property="bottomLineSeparator" destination="KGN-cP-igr" id="YUW-b2-GF8"/>
<outlet property="contactDisplayNameLabel" destination="Lg1-xQ-AGn" id="xKB-Pw-12c"/>
<outlet property="customAccessViewWidthConstraint" destination="pDU-SS-0mb" id="A22-tz-iIz"/>
<outlet property="customAccessoryView" destination="Ogo-Qt-u2C" id="Hec-bO-0Av"/>
<outlet property="customAccessoryViewLeadingConstraint" destination="cWl-2f-cvI" id="WSj-ru-Qxx"/>
<outlet property="lastPresenceLabel" destination="dQt-mN-T6b" id="FYZ-JD-0ck"/>
<outlet property="thumbnailView" destination="RX5-eD-c3c" id="GBi-K9-LhK"/>
<outlet property="topLineSeparator" destination="AqX-al-ZBq" id="jOZ-JS-FEQ"/>
</connections>
</tableViewCell>
</objects>
</document>