mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-30 13:16:58 +02:00
User verification: Update room member details screen with security section and shields.
This commit is contained in:
@@ -18,18 +18,6 @@
|
||||
|
||||
#import "DeviceTableViewCell.h"
|
||||
|
||||
@interface RoomMemberDetailsViewController : MXKRoomMemberDetailsViewController <UIGestureRecognizerDelegate, DeviceTableViewCellDelegate>
|
||||
@interface RoomMemberDetailsViewController : MXKRoomMemberDetailsViewController
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberAvatarHeaderBackground;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomMemberAvatarHeaderBackgroundHeightConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *memberHeaderView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberAvatarMask;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *roomMemberNameLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberNameLabelMask;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel *roomMemberStatusLabel;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *bottomImageView;
|
||||
@end
|
||||
|
||||
|
||||
@@ -33,10 +33,13 @@
|
||||
#define TABLEVIEW_SECTION_HEADER_HEIGHT 28
|
||||
#define TABLEVIEW_SECTION_HEADER_HEIGHT_WHEN_HIDDEN 0.01f
|
||||
|
||||
@interface RoomMemberDetailsViewController () <RoomMemberTitleViewDelegate, DeviceVerificationCoordinatorBridgePresenterDelegate>
|
||||
@interface RoomMemberDetailsViewController () <UIGestureRecognizerDelegate, DeviceTableViewCellDelegate, RoomMemberTitleViewDelegate, DeviceVerificationCoordinatorBridgePresenterDelegate>
|
||||
{
|
||||
RoomMemberTitleView* memberTitleView;
|
||||
|
||||
NSInteger securityIndex;
|
||||
NSMutableArray<NSNumber*> *securityActionsArray;
|
||||
|
||||
/**
|
||||
List of the admin actions on this member.
|
||||
*/
|
||||
@@ -78,6 +81,26 @@
|
||||
*/
|
||||
BOOL isStatusBarHidden;
|
||||
}
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberAvatarHeaderBackground;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomMemberAvatarHeaderBackgroundHeightConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *memberHeaderView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberAvatarMask;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *roomMemberAvatarBadgeImageView;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel *roomMemberNameLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberNameLabelMask;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel *roomMemberStatusLabel;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *bottomImageView;
|
||||
|
||||
|
||||
@property(nonatomic) UserEncryptionTrustLevel encryptionTrustLevel;
|
||||
|
||||
@property(nonatomic, strong) UserVerificationCoordinatorBridgePresenter *userVerificationCoordinatorBridgePresenter;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RoomMemberDetailsViewController
|
||||
@@ -105,6 +128,7 @@
|
||||
// Setup `MXKViewControllerHandling` properties
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
self.encryptionTrustLevel = UserEncryptionTrustLevelUnknown;
|
||||
|
||||
adminActionsArray = [[NSMutableArray alloc] init];
|
||||
otherActionsArray = [[NSMutableArray alloc] init];
|
||||
@@ -195,10 +219,15 @@
|
||||
[self.tableView registerClass:TableViewCellWithButton.class forCellReuseIdentifier:[TableViewCellWithButton defaultReuseIdentifier]];
|
||||
[self.tableView registerClass:RoomTableViewCell.class forCellReuseIdentifier:[RoomTableViewCell defaultReuseIdentifier]];
|
||||
[self.tableView registerClass:DeviceTableViewCell.class forCellReuseIdentifier:[DeviceTableViewCell defaultReuseIdentifier]];
|
||||
[self.tableView registerClass:MXKTableViewCell.class forCellReuseIdentifier:[MXKTableViewCell defaultReuseIdentifier]];
|
||||
|
||||
// Hide line separators of empty cells
|
||||
self.tableView.tableFooterView = [[UIView alloc] init];
|
||||
|
||||
// Enable self sizing cells
|
||||
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
self.tableView.estimatedRowHeight = 50;
|
||||
|
||||
// Observe UIApplicationWillChangeStatusBarOrientationNotification to hide/show bubbles bg.
|
||||
UIApplicationWillChangeStatusBarOrientationNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillChangeStatusBarOrientationNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
@@ -262,6 +291,8 @@
|
||||
// Handle here the bottom image visibility
|
||||
UIInterfaceOrientation screenOrientation = [[UIApplication sharedApplication] statusBarOrientation];
|
||||
self.bottomImageView.hidden = (screenOrientation == UIInterfaceOrientationLandscapeLeft || screenOrientation == UIInterfaceOrientationLandscapeRight);
|
||||
|
||||
[self refreshUserEncryptionTrustLevel];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
@@ -382,14 +413,18 @@
|
||||
|
||||
NSString* presenceText;
|
||||
|
||||
if (self.mxRoomMember.userId)
|
||||
NSString *userId = self.mxRoomMember.userId;
|
||||
|
||||
if (userId)
|
||||
{
|
||||
MXUser *user = [self.mxRoom.mxSession userWithUserId:self.mxRoomMember.userId];
|
||||
MXUser *user = [self.mxRoom.mxSession userWithUserId:userId];
|
||||
presenceText = [Tools presenceText:user];
|
||||
}
|
||||
|
||||
self.roomMemberStatusLabel.text = presenceText;
|
||||
|
||||
self.roomMemberAvatarBadgeImageView.image = self.userEncryptionBadgeImage;
|
||||
|
||||
// Retrieve the existing direct chats
|
||||
[directChatsArray removeAllObjects];
|
||||
NSArray *directRoomIds = self.mainSession.directRooms[self.mxRoomMember.userId];
|
||||
@@ -403,39 +438,134 @@
|
||||
}
|
||||
|
||||
// Retrieve member's devices
|
||||
NSString *userId = self.mxRoomMember.userId;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
[self.mxRoom.mxSession.crypto downloadKeys:@[userId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap, NSDictionary<NSString *,MXCrossSigningInfo *> *crossSigningKeysMap) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
// Restore the status bar
|
||||
typeof(self) self = weakSelf;
|
||||
self->devicesArray = usersDevicesInfoMap.map[userId].allValues;
|
||||
// Reload the full table to take into account a potential change on a device status.
|
||||
[super updateMemberInfo];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[RoomMemberDetailsVC] Crypto failed to download device info for user: %@", userId);
|
||||
if (weakSelf)
|
||||
{
|
||||
// Restore the status bar
|
||||
typeof(self) self = weakSelf;
|
||||
// Notify the end user
|
||||
NSString *myUserId = self.mainSession.myUser.userId;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
|
||||
}
|
||||
|
||||
}];
|
||||
|
||||
if (!RiotSettings.shared.enableCrossSigning)
|
||||
{
|
||||
[self.mxRoom.mxSession.crypto downloadKeys:@[userId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap, NSDictionary<NSString *,MXCrossSigningInfo *> *crossSigningKeysMap) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
// Restore the status bar
|
||||
typeof(self) self = weakSelf;
|
||||
self->devicesArray = usersDevicesInfoMap.map[userId].allValues;
|
||||
// Reload the full table to take into account a potential change on a device status.
|
||||
[super updateMemberInfo];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[RoomMemberDetailsVC] Crypto failed to download device info for user: %@", userId);
|
||||
if (weakSelf)
|
||||
{
|
||||
// Restore the status bar
|
||||
typeof(self) self = weakSelf;
|
||||
// Notify the end user
|
||||
NSString *myUserId = self.mainSession.myUser.userId;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// Complete data update and reload table view
|
||||
[super updateMemberInfo];
|
||||
}
|
||||
|
||||
- (void)refreshUserEncryptionTrustLevel
|
||||
{
|
||||
NSString *userId = self.mxRoomMember.userId;
|
||||
|
||||
if (!userId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.mxRoom.summary.isEncrypted && self.mxRoom.mxSession.crypto)
|
||||
{
|
||||
[self.mxRoom.mxSession.crypto trustLevelSummaryForUserIds:@[userId] success:^(MXUsersTrustLevelSummary *usersTrustLevelSummary) {
|
||||
|
||||
double trustedDevicesPercentage = usersTrustLevelSummary.trustedDevicesProgress.fractionCompleted;
|
||||
|
||||
UserEncryptionTrustLevel userEncryptionTrustLevel;
|
||||
|
||||
if (trustedDevicesPercentage >= 1.0)
|
||||
{
|
||||
userEncryptionTrustLevel = UserEncryptionTrustLevelTrusted;
|
||||
}
|
||||
else if (trustedDevicesPercentage == 0.0)
|
||||
{
|
||||
userEncryptionTrustLevel = UserEncryptionTrustLevelNormal;
|
||||
}
|
||||
else
|
||||
{
|
||||
userEncryptionTrustLevel = UserEncryptionTrustLevelWarning;
|
||||
}
|
||||
|
||||
self.encryptionTrustLevel = userEncryptionTrustLevel;
|
||||
[self updateMemberInfo];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
NSLog(@"[RoomMemberDetailsViewController] Fails to retrieve trust level summary with error: %@", error);
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.encryptionTrustLevel = UserEncryptionTrustLevelNone;
|
||||
[self updateMemberInfo];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage*)userEncryptionBadgeImage
|
||||
{
|
||||
NSString *encryptionIconName;
|
||||
UIImage *encryptionIcon;
|
||||
|
||||
UserEncryptionTrustLevel userEncryptionTrustLevel = self.encryptionTrustLevel;
|
||||
|
||||
switch (userEncryptionTrustLevel) {
|
||||
case RoomEncryptionTrustLevelWarning:
|
||||
encryptionIconName = @"encryption_warning";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelNormal:
|
||||
encryptionIconName = @"encryption_normal";
|
||||
break;
|
||||
case RoomEncryptionTrustLevelTrusted:
|
||||
encryptionIconName = @"encryption_trusted";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (encryptionIconName)
|
||||
{
|
||||
encryptionIcon = [UIImage imageNamed:encryptionIconName];
|
||||
}
|
||||
|
||||
return encryptionIcon;
|
||||
}
|
||||
|
||||
- (BOOL)isRoomMemberCurrentUser
|
||||
{
|
||||
return [self.mxRoomMember.userId isEqualToString:self.mainSession.myUser.userId];
|
||||
}
|
||||
|
||||
- (void)startUserVerification
|
||||
{
|
||||
[[AppDelegate theDelegate] presentUserVerificationForRoomMember:self.mxRoomMember session:self.mainSession];
|
||||
}
|
||||
|
||||
- (void)presentUserVerification
|
||||
{
|
||||
UserVerificationCoordinatorBridgePresenter *userVerificationCoordinatorBridgePresenter = [[UserVerificationCoordinatorBridgePresenter alloc] initWithPresenter:self
|
||||
session:self.mxRoom.mxSession
|
||||
userId:self.mxRoomMember.userId
|
||||
userDisplayName:self.mxRoomMember.displayname];
|
||||
[userVerificationCoordinatorBridgePresenter start];
|
||||
self.userVerificationCoordinatorBridgePresenter = userVerificationCoordinatorBridgePresenter;
|
||||
}
|
||||
|
||||
#pragma mark - Hide/Show navigation bar border
|
||||
|
||||
- (void)hideNavigationBarBorder:(BOOL)isHidden
|
||||
@@ -484,7 +614,7 @@
|
||||
[otherActionsArray removeAllObjects];
|
||||
|
||||
// Consider the case of the user himself
|
||||
if ([self.mxRoomMember.userId isEqualToString:self.mainSession.myUser.userId])
|
||||
if (self.isRoomMemberCurrentUser)
|
||||
{
|
||||
isOneself = YES;
|
||||
|
||||
@@ -618,7 +748,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
adminToolsIndex = otherActionsIndex = directChatsIndex = devicesIndex = -1;
|
||||
securityIndex = adminToolsIndex = otherActionsIndex = directChatsIndex = devicesIndex = -1;
|
||||
|
||||
if (RiotSettings.shared.enableCrossSigning)
|
||||
{
|
||||
securityIndex = sectionCount++;
|
||||
}
|
||||
|
||||
if (otherActionsArray.count)
|
||||
{
|
||||
@@ -644,7 +779,23 @@
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
if (section == adminToolsIndex)
|
||||
if (section == securityIndex)
|
||||
{
|
||||
NSInteger numberOfRows;
|
||||
|
||||
switch (self.encryptionTrustLevel) {
|
||||
case UserEncryptionTrustLevelUnknown:
|
||||
case UserEncryptionTrustLevelNone:
|
||||
numberOfRows = 1;
|
||||
break;
|
||||
default:
|
||||
numberOfRows = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
return numberOfRows;
|
||||
}
|
||||
else if (section == adminToolsIndex)
|
||||
{
|
||||
return adminActionsArray.count;
|
||||
}
|
||||
@@ -666,10 +817,18 @@
|
||||
|
||||
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
if (section == adminToolsIndex)
|
||||
if (section == securityIndex)
|
||||
{
|
||||
return @"SECURITY";
|
||||
}
|
||||
else if (section == adminToolsIndex)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"room_participants_action_section_admin_tools", @"Vector", nil);
|
||||
}
|
||||
else if (RiotSettings.shared.enableCrossSigning && section == otherActionsIndex)
|
||||
{
|
||||
return @"OPTIONS";
|
||||
}
|
||||
else if (section == directChatsIndex)
|
||||
{
|
||||
return NSLocalizedStringFromTable(@"room_participants_action_section_direct_chats", @"Vector", nil);
|
||||
@@ -741,7 +900,78 @@
|
||||
{
|
||||
UITableViewCell *cell;
|
||||
|
||||
if (indexPath.section == adminToolsIndex || indexPath.section == otherActionsIndex)
|
||||
if (indexPath.section == securityIndex)
|
||||
{
|
||||
if (indexPath.row == [self tableView:self.tableView numberOfRowsInSection:indexPath.section] - 1)
|
||||
{
|
||||
MXKTableViewCell *encryptionInfoCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
NSMutableString *encryptionInformation = [NSMutableString new];
|
||||
|
||||
switch (self.encryptionTrustLevel) {
|
||||
case UserEncryptionTrustLevelUnknown:
|
||||
[encryptionInformation appendString:@"Loading"];
|
||||
break;
|
||||
case UserEncryptionTrustLevelNone:
|
||||
[encryptionInformation appendString:@"Messages in this room are not end-to-end encrypted."];
|
||||
break;
|
||||
default:
|
||||
[encryptionInformation appendString:@"Messages in this room are end-to-end encrypted.\n\nYour messages are secured with locks and only you and the recipient have the unique keys to unlock them."];
|
||||
break;
|
||||
}
|
||||
|
||||
[encryptionInformation appendString:@"\n"];
|
||||
|
||||
encryptionInfoCell.textLabel.backgroundColor = [UIColor clearColor];
|
||||
encryptionInfoCell.textLabel.numberOfLines = 0;
|
||||
encryptionInfoCell.textLabel.text = encryptionInformation;
|
||||
encryptionInfoCell.textLabel.font = [UIFont systemFontOfSize:14.0];
|
||||
encryptionInfoCell.textLabel.textColor = ThemeService.shared.theme.headerTextPrimaryColor;
|
||||
|
||||
encryptionInfoCell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
encryptionInfoCell.accessoryType = UITableViewCellAccessoryNone;
|
||||
encryptionInfoCell.contentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
|
||||
encryptionInfoCell.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
|
||||
|
||||
cell = encryptionInfoCell;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXKTableViewCell *securityStatusCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
NSString *statusText;
|
||||
|
||||
switch (self.encryptionTrustLevel) {
|
||||
case UserEncryptionTrustLevelTrusted:
|
||||
statusText = @"Verified";
|
||||
break;
|
||||
case UserEncryptionTrustLevelNormal:
|
||||
statusText = @"Verify";
|
||||
break;
|
||||
case UserEncryptionTrustLevelWarning:
|
||||
statusText = @"Warning";
|
||||
break;
|
||||
default:
|
||||
statusText = @"Loading";
|
||||
break;
|
||||
}
|
||||
|
||||
securityStatusCell.imageView.image = self.userEncryptionBadgeImage;
|
||||
|
||||
securityStatusCell.textLabel.numberOfLines = 1;
|
||||
securityStatusCell.textLabel.font = [UIFont systemFontOfSize:16.0];
|
||||
securityStatusCell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
securityStatusCell.textLabel.text = statusText;
|
||||
|
||||
securityStatusCell.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
securityStatusCell.contentView.backgroundColor = [UIColor clearColor];
|
||||
securityStatusCell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
securityStatusCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
|
||||
cell = securityStatusCell;
|
||||
}
|
||||
}
|
||||
else if (indexPath.section == adminToolsIndex || indexPath.section == otherActionsIndex)
|
||||
{
|
||||
TableViewCellWithButton *cellWithButton = [tableView dequeueReusableCellWithIdentifier:[TableViewCellWithButton defaultReuseIdentifier] forIndexPath:indexPath];
|
||||
|
||||
@@ -852,26 +1082,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section == directChatsIndex)
|
||||
{
|
||||
return [RoomTableViewCell cellHeight];
|
||||
}
|
||||
else if (indexPath.section == devicesIndex)
|
||||
{
|
||||
if (indexPath.row < devicesArray.count)
|
||||
{
|
||||
return [DeviceTableViewCell cellHeightWithDeviceInfo:devicesArray[indexPath.row] andCellWidth:self.tableView.frame.size.width];
|
||||
}
|
||||
}
|
||||
|
||||
return TABLEVIEW_ROW_CELL_HEIGHT;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
if (section == otherActionsIndex)
|
||||
if (!RiotSettings.shared.enableCrossSigning && section == otherActionsIndex)
|
||||
{
|
||||
return TABLEVIEW_SECTION_HEADER_HEIGHT_WHEN_HIDDEN;
|
||||
}
|
||||
@@ -881,7 +1094,18 @@
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section == directChatsIndex)
|
||||
if (indexPath.section == securityIndex)
|
||||
{
|
||||
if (self.encryptionTrustLevel == UserEncryptionTrustLevelNormal)
|
||||
{
|
||||
[self startUserVerification];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self presentUserVerification];
|
||||
}
|
||||
}
|
||||
else if (indexPath.section == directChatsIndex)
|
||||
{
|
||||
if (indexPath.row < directChatsArray.count)
|
||||
{
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@@ -15,6 +14,7 @@
|
||||
<outlet property="bottomImageView" destination="7Dc-jk-9sT" id="BVN-bt-VXI"/>
|
||||
<outlet property="memberHeaderView" destination="YXr-As-Mqh" id="Eqb-qr-iAo"/>
|
||||
<outlet property="memberThumbnail" destination="GQ1-rP-ckr" id="abr-hr-C3p"/>
|
||||
<outlet property="roomMemberAvatarBadgeImageView" destination="jHh-A3-In3" id="LKN-mv-WFg"/>
|
||||
<outlet property="roomMemberAvatarHeaderBackground" destination="ouj-VM-zdT" id="YeD-zt-8y5"/>
|
||||
<outlet property="roomMemberAvatarHeaderBackgroundHeightConstraint" destination="dBL-G6-Yec" id="QXZ-ZP-0Rn"/>
|
||||
<outlet property="roomMemberAvatarMask" destination="MAS-3M-3cg" id="nLI-7d-5Hu"/>
|
||||
@@ -45,7 +45,7 @@
|
||||
<rect key="frame" x="137.5" y="0.0" width="100" height="125"/>
|
||||
<subviews>
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GQ1-rP-ckr" customClass="MXKImageView">
|
||||
<rect key="frame" x="7.5" y="31" width="84" height="84"/>
|
||||
<rect key="frame" x="8" y="31" width="84" height="84"/>
|
||||
<color key="backgroundColor" red="0.6886889638" green="1" blue="0.74383144840000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="MemberAvatar"/>
|
||||
<constraints>
|
||||
@@ -53,11 +53,21 @@
|
||||
<constraint firstAttribute="width" secondItem="GQ1-rP-ckr" secondAttribute="height" multiplier="1:1" id="a1T-Y0-Iic"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="jHh-A3-In3">
|
||||
<rect key="frame" x="68" y="91" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="jHh-A3-In3" secondAttribute="height" multiplier="1:1" id="fvP-Hk-apc"/>
|
||||
<constraint firstAttribute="width" constant="24" id="gBW-ym-4Qv"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="RoomMemberDetailsVCAvatarMask"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="GQ1-rP-ckr" secondAttribute="bottom" constant="10" id="3pC-So-WvO"/>
|
||||
<constraint firstItem="jHh-A3-In3" firstAttribute="bottom" secondItem="GQ1-rP-ckr" secondAttribute="bottom" id="6Gg-lp-pJw"/>
|
||||
<constraint firstItem="jHh-A3-In3" firstAttribute="trailing" secondItem="GQ1-rP-ckr" secondAttribute="trailing" id="TbA-vY-3Ef"/>
|
||||
<constraint firstItem="jHh-A3-In3" firstAttribute="width" secondItem="jHh-A3-In3" secondAttribute="height" multiplier="1:1" id="Ua2-xg-Vd2"/>
|
||||
<constraint firstItem="GQ1-rP-ckr" firstAttribute="centerX" secondItem="MAS-3M-3cg" secondAttribute="centerX" id="ZGI-nR-gGx"/>
|
||||
<constraint firstAttribute="width" constant="100" id="fwv-qE-IV1"/>
|
||||
</constraints>
|
||||
|
||||
Reference in New Issue
Block a user