mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-19 06:02:12 +02:00
Merge remote-tracking branch 'origin/develop' into public_rooms_search
Conflicts: Vector/Utils/AvatarGenerator.m Vector/ViewController/HomeViewController.m
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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...";
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user