1351 lines
56 KiB
Objective-C
1351 lines
56 KiB
Objective-C
/*
|
|
Copyright 2018-2024 New Vector Ltd.
|
|
Copyright 2017 Vector Creations Ltd
|
|
Copyright 2016 OpenMarket Ltd
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
Please see LICENSE in the repository root for full details.
|
|
*/
|
|
|
|
#import "RoomMemberDetailsViewController.h"
|
|
|
|
#import "GeneratedInterface-Swift.h"
|
|
|
|
#import "RoomMemberTitleView.h"
|
|
|
|
#import "AvatarGenerator.h"
|
|
#import "Tools.h"
|
|
|
|
#import "TableViewCellWithButton.h"
|
|
#import "RoomTableViewCell.h"
|
|
#import "MXRoom+Riot.h"
|
|
|
|
#define TABLEVIEW_ROW_CELL_HEIGHT 46
|
|
#define TABLEVIEW_SECTION_HEADER_HEIGHT 28
|
|
|
|
@interface RoomMemberDetailsViewController () <UIGestureRecognizerDelegate, DeviceTableViewCellDelegate, RoomMemberTitleViewDelegate, KeyVerificationCoordinatorBridgePresenterDelegate, UserVerificationCoordinatorBridgePresenterDelegate>
|
|
{
|
|
RoomMemberTitleView* memberTitleView;
|
|
|
|
NSInteger securityIndex;
|
|
NSArray<NSNumber*> *securityActionsArray;
|
|
|
|
/**
|
|
List of the admin actions on this member.
|
|
*/
|
|
NSMutableArray<NSNumber*> *adminActionsArray;
|
|
NSInteger adminToolsIndex;
|
|
|
|
/**
|
|
List of the basic actions on this member.
|
|
*/
|
|
NSMutableArray<NSNumber*> *otherActionsArray;
|
|
NSInteger otherActionsIndex;
|
|
|
|
/**
|
|
List of the direct chats (room ids) with this member.
|
|
*/
|
|
NSMutableArray<NSString*> *directChatsArray;
|
|
NSInteger directChatsIndex;
|
|
|
|
/**
|
|
Devices
|
|
*/
|
|
NSArray<MXDeviceInfo *> *devicesArray;
|
|
NSInteger devicesIndex;
|
|
KeyVerificationCoordinatorBridgePresenter *keyVerificationCoordinatorBridgePresenter;
|
|
|
|
|
|
/**
|
|
Observe UIApplicationWillChangeStatusBarOrientationNotification to hide/show bubbles bg.
|
|
*/
|
|
id UIApplicationWillChangeStatusBarOrientationNotificationObserver;
|
|
|
|
/**
|
|
Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
|
|
*/
|
|
id kThemeServiceDidChangeThemeNotificationObserver;
|
|
|
|
/**
|
|
The current visibility of the status bar in this view controller.
|
|
*/
|
|
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 *roomMemberNameContainerView;
|
|
|
|
@property (weak, nonatomic) IBOutlet UILabel *roomMemberUserIdLabel;
|
|
|
|
@property (weak, nonatomic) IBOutlet UILabel *roomMemberStatusLabel;
|
|
|
|
@property (weak, nonatomic) IBOutlet UIImageView *bottomImageView;
|
|
|
|
@property (weak, nonatomic) IBOutlet UILabel *roomMemberPowerLevelLabel;
|
|
@property (weak, nonatomic) IBOutlet UIView *roomMemberPowerLevelContainerView;
|
|
|
|
@property(nonatomic) UserEncryptionTrustLevel encryptionTrustLevel;
|
|
|
|
@property(nonatomic, strong) UserVerificationCoordinatorBridgePresenter *userVerificationCoordinatorBridgePresenter;
|
|
|
|
@property(nonatomic) AnalyticsScreenTracker *screenTracker;
|
|
|
|
@end
|
|
|
|
@implementation RoomMemberDetailsViewController
|
|
|
|
#pragma mark - Class methods
|
|
|
|
+ (UINib *)nib
|
|
{
|
|
return [UINib nibWithNibName:NSStringFromClass(self.class)
|
|
bundle:[NSBundle bundleForClass:self.class]];
|
|
}
|
|
|
|
+ (instancetype)roomMemberDetailsViewController
|
|
{
|
|
return [[[self class] alloc] initWithNibName:NSStringFromClass(self.class)
|
|
bundle:[NSBundle bundleForClass:self.class]];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)finalizeInit
|
|
{
|
|
[super finalizeInit];
|
|
|
|
// Setup `MXKViewControllerHandling` properties
|
|
self.enableBarTintColorStatusChange = NO;
|
|
self.rageShakeManager = [RageShakeManager sharedManager];
|
|
self.encryptionTrustLevel = UserEncryptionTrustLevelUnknown;
|
|
|
|
adminActionsArray = [[NSMutableArray alloc] init];
|
|
otherActionsArray = [[NSMutableArray alloc] init];
|
|
directChatsArray = [[NSMutableArray alloc] init];
|
|
|
|
// Keep visible the status bar by default.
|
|
isStatusBarHidden = NO;
|
|
|
|
self.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenUser];
|
|
}
|
|
|
|
- (void)viewDidLoad
|
|
{
|
|
[super viewDidLoad];
|
|
// Do any additional setup after loading the view, typically from a nib.
|
|
|
|
memberTitleView = [RoomMemberTitleView roomMemberTitleView];
|
|
memberTitleView.delegate = self;
|
|
|
|
// Define directly the navigation titleView with the custom title view instance. Do not use anymore a container.
|
|
self.navigationItem.titleView = memberTitleView;
|
|
|
|
// Add tap to show the room member avatar in fullscreen
|
|
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
|
|
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
|
|
[tap setNumberOfTouchesRequired:1];
|
|
[tap setNumberOfTapsRequired:1];
|
|
[tap setDelegate:self];
|
|
[self.roomMemberAvatarMask addGestureRecognizer:tap];
|
|
self.roomMemberAvatarMask.userInteractionEnabled = YES;
|
|
|
|
// Need to listen to the tap gesture in the title view too.
|
|
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
|
|
[tap setNumberOfTouchesRequired:1];
|
|
[tap setNumberOfTapsRequired:1];
|
|
[tap setDelegate:self];
|
|
[memberTitleView.memberAvatarMask addGestureRecognizer:tap];
|
|
memberTitleView.memberAvatarMask.userInteractionEnabled = YES;
|
|
|
|
// Register collection view cell class
|
|
[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) {
|
|
|
|
NSNumber *orientation = (NSNumber*)(notif.userInfo[UIApplicationStatusBarOrientationUserInfoKey]);
|
|
self.bottomImageView.hidden = (orientation.integerValue == UIInterfaceOrientationLandscapeLeft || orientation.integerValue == UIInterfaceOrientationLandscapeRight);
|
|
}];
|
|
|
|
// Observe user interface theme change.
|
|
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
|
|
|
[self userInterfaceThemeDidChange];
|
|
|
|
}];
|
|
}
|
|
|
|
- (void)userInterfaceThemeDidChange
|
|
{
|
|
[ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar];
|
|
self.navigationController.navigationBar.translucent = YES;
|
|
|
|
self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor;
|
|
|
|
self.memberHeaderView.backgroundColor = ThemeService.shared.theme.baseColor;
|
|
self.roomMemberNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
|
self.roomMemberUserIdLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
|
|
|
self.roomMemberStatusLabel.textColor = ThemeService.shared.theme.tintColor;
|
|
self.roomMemberPowerLevelLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
|
|
|
// Check the table view style to select its bg color.
|
|
self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor);
|
|
self.view.backgroundColor = self.tableView.backgroundColor;
|
|
self.tableView.separatorColor = ThemeService.shared.theme.lineBreakColor;
|
|
|
|
if (self.tableView.dataSource)
|
|
{
|
|
[self.tableView reloadData];
|
|
}
|
|
|
|
[self setNeedsStatusBarAppearanceUpdate];
|
|
}
|
|
|
|
- (UIStatusBarStyle)preferredStatusBarStyle
|
|
{
|
|
return ThemeService.shared.theme.statusBarStyle;
|
|
}
|
|
|
|
- (BOOL)prefersStatusBarHidden
|
|
{
|
|
// Return the current status bar visibility.
|
|
return isStatusBarHidden;
|
|
}
|
|
|
|
- (void)viewWillAppear:(BOOL)animated
|
|
{
|
|
[super viewWillAppear:animated];
|
|
|
|
[self.screenTracker trackScreen];
|
|
|
|
[self userInterfaceThemeDidChange];
|
|
|
|
// Hide the bottom border of the navigation bar to display the expander header
|
|
[self hideNavigationBarBorder:YES];
|
|
|
|
// Handle here the bottom image visibility
|
|
UIInterfaceOrientation screenOrientation = [[UIApplication sharedApplication] statusBarOrientation];
|
|
self.bottomImageView.hidden = (screenOrientation == UIInterfaceOrientationLandscapeLeft || screenOrientation == UIInterfaceOrientationLandscapeRight);
|
|
|
|
[self refreshUserEncryptionTrustLevel];
|
|
}
|
|
|
|
- (void)viewWillDisappear:(BOOL)animated
|
|
{
|
|
[super viewWillDisappear:animated];
|
|
|
|
// Restore navigation bar display
|
|
[self hideNavigationBarBorder:NO];
|
|
|
|
self.bottomImageView.hidden = YES;
|
|
}
|
|
|
|
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
|
|
{
|
|
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
|
|
|
// Restore navigation bar display
|
|
[self hideNavigationBarBorder:NO];
|
|
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(coordinator.transitionDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
|
// Hide the bottom border of the navigation bar
|
|
[self hideNavigationBarBorder:YES];
|
|
|
|
});
|
|
}
|
|
|
|
- (void)destroy
|
|
{
|
|
[super destroy];
|
|
|
|
adminActionsArray = nil;
|
|
otherActionsArray = nil;
|
|
directChatsArray = nil;
|
|
devicesArray = nil;
|
|
|
|
if (UIApplicationWillChangeStatusBarOrientationNotificationObserver)
|
|
{
|
|
[[NSNotificationCenter defaultCenter] removeObserver:UIApplicationWillChangeStatusBarOrientationNotificationObserver];
|
|
UIApplicationWillChangeStatusBarOrientationNotificationObserver = nil;
|
|
}
|
|
|
|
if (kThemeServiceDidChangeThemeNotificationObserver)
|
|
{
|
|
[[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver];
|
|
kThemeServiceDidChangeThemeNotificationObserver = nil;
|
|
}
|
|
|
|
[memberTitleView removeFromSuperview];
|
|
memberTitleView = nil;
|
|
}
|
|
|
|
- (void)viewDidLayoutSubviews
|
|
{
|
|
[super viewDidLayoutSubviews];
|
|
|
|
// Check whether the title view has been created and rendered.
|
|
if (memberTitleView && memberTitleView.superview)
|
|
{
|
|
// Adjust the header height by taking into account the actual position of the member avatar in title view
|
|
// This position depends automatically on the screen orientation.
|
|
CGPoint memberAvatarOriginInTitleView = memberTitleView.memberAvatarMask.frame.origin;
|
|
CGPoint memberAvatarActualPosition = [memberTitleView convertPoint:memberAvatarOriginInTitleView toView:self.view];
|
|
|
|
CGFloat avatarHeaderHeight = memberAvatarActualPosition.y + self.memberThumbnail.frame.size.height;
|
|
if (_roomMemberAvatarHeaderBackgroundHeightConstraint.constant != avatarHeaderHeight)
|
|
{
|
|
_roomMemberAvatarHeaderBackgroundHeightConstraint.constant = avatarHeaderHeight;
|
|
|
|
// Force the layout of the header
|
|
[self.memberHeaderView layoutIfNeeded];
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (UIImage*)picturePlaceholder
|
|
{
|
|
if (self.mxRoomMember)
|
|
{
|
|
// Use the vector style placeholder
|
|
return [AvatarGenerator generateAvatarForMatrixItem:self.mxRoomMember.userId withDisplayName:self.mxRoomMember.displayname];
|
|
}
|
|
|
|
return [MXKTools paintImage:AssetImages.placeholder.image
|
|
withColor:ThemeService.shared.theme.tintColor];
|
|
}
|
|
|
|
- (void)updateMemberInfo
|
|
{
|
|
if (self.mxRoomMember)
|
|
{
|
|
self.roomMemberNameContainerView.hidden = !self.mxRoomMember.displayname;
|
|
|
|
self.roomMemberNameLabel.text = self.mxRoomMember.displayname;
|
|
|
|
self.roomMemberUserIdLabel.text = self.mxRoomMember.userId;
|
|
|
|
// Update member power level
|
|
MXWeakify(self);
|
|
[self.mxRoom state:^(MXRoomState *roomState) {
|
|
MXStrongifyAndReturnIfNil(self);
|
|
|
|
MXRoomPowerLevels *powerLevels = [roomState powerLevels];
|
|
NSInteger powerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxRoomMember.userId];
|
|
|
|
RoomPowerLevel roomPowerLevel = [RoomPowerLevelHelper roomPowerLevelFrom:powerLevel];
|
|
|
|
switch (roomPowerLevel) {
|
|
case RoomPowerLevelAdmin:
|
|
self.roomMemberPowerLevelLabel.text = [VectorL10n roomMemberPowerLevelAdminIn:self.mxRoom.summary.displayName];
|
|
self.roomMemberPowerLevelContainerView.hidden = NO;
|
|
break;
|
|
case RoomPowerLevelModerator:
|
|
self.roomMemberPowerLevelLabel.text = [VectorL10n roomMemberPowerLevelModeratorIn:self.mxRoom.summary.displayName];
|
|
self.roomMemberPowerLevelContainerView.hidden = NO;
|
|
break;
|
|
default:
|
|
self.roomMemberPowerLevelLabel.text = nil;
|
|
self.roomMemberPowerLevelContainerView.hidden = YES;
|
|
break;
|
|
}
|
|
}];
|
|
|
|
NSString* presenceText;
|
|
|
|
NSString *userId = self.mxRoomMember.userId;
|
|
|
|
if (userId)
|
|
{
|
|
MXUser *user = [self.mxRoom.mxSession userWithUserId:userId];
|
|
presenceText = [Tools presenceText:user];
|
|
}
|
|
|
|
self.roomMemberStatusLabel.text = presenceText;
|
|
|
|
self.roomMemberAvatarBadgeImageView.image = [EncryptionTrustLevelBadgeImageHelper userBadgeImageFor:self.encryptionTrustLevel];
|
|
|
|
// Retrieve the existing direct chats
|
|
[directChatsArray removeAllObjects];
|
|
NSArray *directRoomIds = self.mainSession.directRooms[self.mxRoomMember.userId];
|
|
// Check whether the room is still existing
|
|
for (NSString* directRoomId in directRoomIds)
|
|
{
|
|
if ([self.mainSession roomWithRoomId:directRoomId])
|
|
{
|
|
[directChatsArray addObject:directRoomId];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Complete data update and reload table view
|
|
[super updateMemberInfo];
|
|
}
|
|
|
|
- (void)refreshUserEncryptionTrustLevel
|
|
{
|
|
NSString *userId = self.mxRoomMember.userId;
|
|
|
|
if (!userId)
|
|
{
|
|
return;
|
|
}
|
|
|
|
[self.mxRoom.mxSession.crypto downloadKeys:@[userId] forceDownload:YES success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap, NSDictionary<NSString *,MXCrossSigningInfo *> *crossSigningKeysMap) {
|
|
[self.mxRoom encryptionTrustLevelForUserId:userId onComplete:^(UserEncryptionTrustLevel userEncryptionTrustLevel) {
|
|
self.encryptionTrustLevel = userEncryptionTrustLevel;
|
|
[self updateMemberInfo];
|
|
}];
|
|
} failure:^(NSError *error) {
|
|
[self.mxRoom encryptionTrustLevelForUserId:userId onComplete:^(UserEncryptionTrustLevel userEncryptionTrustLevel) {
|
|
self.encryptionTrustLevel = userEncryptionTrustLevel;
|
|
[self updateMemberInfo];
|
|
}];
|
|
}];
|
|
}
|
|
|
|
- (BOOL)isRoomMemberCurrentUser
|
|
{
|
|
return [self.mxRoomMember.userId isEqualToString:self.mainSession.myUser.userId];
|
|
}
|
|
|
|
- (void)startUserVerification
|
|
{
|
|
[[AppDelegate theDelegate] presentUserVerificationForRoomMember:self.mxRoomMember session:self.mainSession completion:^{
|
|
[self refreshUserEncryptionTrustLevel];
|
|
}];
|
|
}
|
|
|
|
- (void)presentUserVerification
|
|
{
|
|
UserVerificationCoordinatorBridgePresenter *userVerificationCoordinatorBridgePresenter = [[UserVerificationCoordinatorBridgePresenter alloc] initWithPresenter:self
|
|
session:self.mxRoom.mxSession
|
|
userId:self.mxRoomMember.userId
|
|
userDisplayName:self.mxRoomMember.displayname];
|
|
userVerificationCoordinatorBridgePresenter.delegate = self;
|
|
[userVerificationCoordinatorBridgePresenter start];
|
|
self.userVerificationCoordinatorBridgePresenter = userVerificationCoordinatorBridgePresenter;
|
|
}
|
|
|
|
- (void)presentCompleteSecurity
|
|
{
|
|
[[AppDelegate theDelegate] presentCompleteSecurityForSession:self.mainSession];
|
|
}
|
|
|
|
- (void)showRoomWithId:(NSString*)roomId
|
|
{
|
|
Analytics.shared.viewRoomTrigger = AnalyticsViewRoomTriggerRoomMemberDetail;
|
|
[[AppDelegate theDelegate] showRoom:roomId andEventId:nil withMatrixSession:self.mainSession];
|
|
}
|
|
|
|
#pragma mark - Hide/Show navigation bar border
|
|
|
|
- (void)hideNavigationBarBorder:(BOOL)isHidden
|
|
{
|
|
// Consider the main navigation controller if the current view controller is embedded inside a split view controller.
|
|
UINavigationController *mainNavigationController = self.navigationController;
|
|
if (self.splitViewController && self.splitViewController.isCollapsed && self.splitViewController.viewControllers.count)
|
|
{
|
|
mainNavigationController = self.splitViewController.viewControllers.firstObject;
|
|
}
|
|
|
|
if (isHidden)
|
|
{
|
|
// The default shadow image is nil. When non-nil, this property represents a custom shadow image to show instead
|
|
// of the default. For a custom shadow image to be shown, a custom background image must also be set with the
|
|
// setBackgroundImage:forBarMetrics: method. If the default background image is used, then the default shadow
|
|
// image will be used regardless of the value of this property.
|
|
[mainNavigationController.navigationBar setShadowImage:[[UIImage alloc] init]];
|
|
[mainNavigationController.navigationBar setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault];
|
|
}
|
|
else
|
|
{
|
|
// Restore default navigationbar settings
|
|
[mainNavigationController.navigationBar setShadowImage:nil];
|
|
[mainNavigationController.navigationBar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
|
|
}
|
|
|
|
// Main Navigation bar opacity must follow
|
|
self.navigationController.navigationBar.translucent = isHidden;
|
|
mainNavigationController.navigationBar.translucent = isHidden;
|
|
}
|
|
|
|
#pragma mark - TableView data source
|
|
|
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
|
{
|
|
NSInteger sectionCount = 0;
|
|
|
|
BOOL isOneself = NO;
|
|
|
|
// Check user's power level before allowing an action (kick, ban, ...)
|
|
MXRoomPowerLevels *powerLevels = [self.mxRoom.dangerousSyncState powerLevels];
|
|
NSInteger memberPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxRoomMember.userId];
|
|
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
|
|
|
|
[adminActionsArray removeAllObjects];
|
|
[otherActionsArray removeAllObjects];
|
|
|
|
// Consider the case of the user himself
|
|
if (self.isRoomMemberCurrentUser)
|
|
{
|
|
isOneself = YES;
|
|
|
|
if (self.enableLeave)
|
|
{
|
|
[otherActionsArray addObject:@(MXKRoomMemberDetailsActionLeave)];
|
|
}
|
|
|
|
if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomPowerLevels])
|
|
{
|
|
// Check whether the user is admin (in this case he may reduce his power level to become moderator or less, EXCEPT if he is the only admin).
|
|
if (oneSelfPowerLevel >= RoomPowerLevelAdmin)
|
|
{
|
|
NSArray *levelValues = powerLevels.users.allValues;
|
|
NSUInteger adminCount = 0;
|
|
for (NSNumber *valueNumber in levelValues)
|
|
{
|
|
if ([valueNumber unsignedIntegerValue] >= RoomPowerLevelAdmin)
|
|
{
|
|
adminCount ++;
|
|
}
|
|
}
|
|
|
|
if (adminCount > 1)
|
|
{
|
|
[adminActionsArray addObject:@(MXKRoomMemberDetailsActionSetModerator)];
|
|
[adminActionsArray addObject:@(MXKRoomMemberDetailsActionSetDefaultPowerLevel)];
|
|
}
|
|
}
|
|
// Check whether the user is moderator (in this case he may reduce his power level to become normal user).
|
|
else if (oneSelfPowerLevel >= RoomPowerLevelModerator)
|
|
{
|
|
[adminActionsArray addObject:@(MXKRoomMemberDetailsActionSetDefaultPowerLevel)];
|
|
}
|
|
}
|
|
}
|
|
else if (self.mxRoomMember)
|
|
{
|
|
// Enumerate admin actions
|
|
switch (self.mxRoomMember.membership)
|
|
{
|
|
case MXMembershipInvite:
|
|
case MXMembershipJoin:
|
|
{
|
|
// update power level
|
|
if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomPowerLevels] && oneSelfPowerLevel > memberPowerLevel)
|
|
{
|
|
// Check whether user is admin
|
|
if (oneSelfPowerLevel >= RoomPowerLevelAdmin)
|
|
{
|
|
[adminActionsArray addObject:@(MXKRoomMemberDetailsActionSetAdmin)];
|
|
}
|
|
|
|
// Check whether the member may become moderator
|
|
if (oneSelfPowerLevel >= RoomPowerLevelModerator && memberPowerLevel < RoomPowerLevelModerator)
|
|
{
|
|
[adminActionsArray addObject:@(MXKRoomMemberDetailsActionSetModerator)];
|
|
}
|
|
|
|
if (memberPowerLevel >= RoomPowerLevelModerator)
|
|
{
|
|
[adminActionsArray addObject:@(MXKRoomMemberDetailsActionSetDefaultPowerLevel)];
|
|
}
|
|
}
|
|
|
|
// Check conditions to be able to kick someone
|
|
if (oneSelfPowerLevel >= [powerLevels kick] && oneSelfPowerLevel > memberPowerLevel)
|
|
{
|
|
[adminActionsArray addObject:@(MXKRoomMemberDetailsActionKick)];
|
|
}
|
|
// Check conditions to be able to ban someone
|
|
if (oneSelfPowerLevel >= [powerLevels ban] && oneSelfPowerLevel > memberPowerLevel)
|
|
{
|
|
[adminActionsArray addObject:@(MXKRoomMemberDetailsActionBan)];
|
|
}
|
|
|
|
break;
|
|
}
|
|
case MXMembershipLeave:
|
|
{
|
|
// Check conditions to be able to invite someone
|
|
if (oneSelfPowerLevel >= [powerLevels invite])
|
|
{
|
|
[adminActionsArray addObject:@(MXKRoomMemberDetailsActionInvite)];
|
|
}
|
|
// Check conditions to be able to ban someone
|
|
if (oneSelfPowerLevel >= [powerLevels ban] && oneSelfPowerLevel > memberPowerLevel)
|
|
{
|
|
[adminActionsArray addObject:@(MXKRoomMemberDetailsActionBan)];
|
|
}
|
|
break;
|
|
}
|
|
case MXMembershipBan:
|
|
{
|
|
// Check conditions to be able to unban someone
|
|
if (oneSelfPowerLevel >= [powerLevels ban] && oneSelfPowerLevel > memberPowerLevel)
|
|
{
|
|
[adminActionsArray addObject:@(MXKRoomMemberDetailsActionUnban)];
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// List the other actions
|
|
if (self.enableVoipCall)
|
|
{
|
|
// Offer voip call options
|
|
[otherActionsArray addObject:@(MXKRoomMemberDetailsActionStartVoiceCall)];
|
|
[otherActionsArray addObject:@(MXKRoomMemberDetailsActionStartVideoCall)];
|
|
}
|
|
|
|
// Check whether the option Ignore may be presented
|
|
if (RiotSettings.shared.roomMemberScreenShowIgnore)
|
|
{
|
|
// is he already ignored ?
|
|
if (![self.mainSession isUserIgnored:self.mxRoomMember.userId])
|
|
{
|
|
[otherActionsArray addObject:@(MXKRoomMemberDetailsActionIgnore)];
|
|
}
|
|
else
|
|
{
|
|
[otherActionsArray addObject:@(MXKRoomMemberDetailsActionUnignore)];
|
|
}
|
|
}
|
|
|
|
if (self.enableMention)
|
|
{
|
|
// Add mention option
|
|
[otherActionsArray addObject:@(MXKRoomMemberDetailsActionMention)];
|
|
}
|
|
}
|
|
|
|
if (self.mxRoom.summary.isEncrypted)
|
|
{
|
|
securityActionsArray = @[@(MXKRoomMemberDetailsActionSecurity),
|
|
@(MXKRoomMemberDetailsActionSecurityInformation)];
|
|
|
|
}
|
|
else
|
|
{
|
|
securityActionsArray = @[@(MXKRoomMemberDetailsActionSecurity)];
|
|
}
|
|
|
|
securityIndex = adminToolsIndex = otherActionsIndex = directChatsIndex = devicesIndex = -1;
|
|
|
|
|
|
if (securityActionsArray.count)
|
|
{
|
|
securityIndex = sectionCount++;
|
|
}
|
|
|
|
if (otherActionsArray.count)
|
|
{
|
|
otherActionsIndex = sectionCount++;
|
|
}
|
|
if (adminActionsArray.count)
|
|
{
|
|
adminToolsIndex = sectionCount++;
|
|
}
|
|
|
|
if (!isOneself)
|
|
{
|
|
directChatsIndex = sectionCount++;
|
|
}
|
|
|
|
if (devicesArray.count)
|
|
{
|
|
devicesIndex = sectionCount++;
|
|
}
|
|
|
|
return sectionCount;
|
|
}
|
|
|
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
|
{
|
|
if (section == securityIndex)
|
|
{
|
|
return securityActionsArray.count;
|
|
}
|
|
else if (section == adminToolsIndex)
|
|
{
|
|
return adminActionsArray.count;
|
|
}
|
|
else if (section == otherActionsIndex)
|
|
{
|
|
return otherActionsArray.count;
|
|
}
|
|
else if (section == directChatsIndex)
|
|
{
|
|
return (directChatsArray.count + 1);
|
|
}
|
|
else if (section == devicesIndex)
|
|
{
|
|
return (devicesArray.count);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
|
|
{
|
|
if (section == securityIndex)
|
|
{
|
|
return [VectorL10n roomParticipantsActionSectionSecurity];
|
|
}
|
|
else if (section == adminToolsIndex)
|
|
{
|
|
return [VectorL10n roomParticipantsActionSectionAdminTools];
|
|
}
|
|
else if (section == otherActionsIndex)
|
|
{
|
|
return [VectorL10n roomParticipantsActionSectionOther];
|
|
}
|
|
else if (section == directChatsIndex)
|
|
{
|
|
return [VectorL10n roomParticipantsActionSectionDirectChats];
|
|
}
|
|
else if (section == devicesIndex)
|
|
{
|
|
return [VectorL10n roomParticipantsActionSectionDevices];
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (NSString*)actionButtonTitle:(MXKRoomMemberDetailsAction)action
|
|
{
|
|
NSString *title;
|
|
|
|
switch (action)
|
|
{
|
|
case MXKRoomMemberDetailsActionInvite:
|
|
title = [VectorL10n roomParticipantsActionInvite];
|
|
break;
|
|
case MXKRoomMemberDetailsActionLeave:
|
|
title = [VectorL10n roomParticipantsActionLeave];
|
|
break;
|
|
case MXKRoomMemberDetailsActionKick:
|
|
if (self.mxRoom.summary.roomType == MXRoomTypeSpace)
|
|
{
|
|
title = [VectorL10n spaceParticipantsActionRemove];
|
|
}
|
|
else
|
|
{
|
|
title = [VectorL10n roomParticipantsActionRemove];
|
|
}
|
|
break;
|
|
case MXKRoomMemberDetailsActionBan:
|
|
if (self.mxRoom.summary.roomType == MXRoomTypeSpace)
|
|
{
|
|
title = [VectorL10n spaceParticipantsActionBan];
|
|
}
|
|
else
|
|
{
|
|
title = [VectorL10n roomParticipantsActionBan];
|
|
}
|
|
break;
|
|
case MXKRoomMemberDetailsActionUnban:
|
|
title = [VectorL10n roomParticipantsActionUnban];
|
|
break;
|
|
case MXKRoomMemberDetailsActionIgnore:
|
|
title = [VectorL10n roomParticipantsActionIgnore];
|
|
break;
|
|
case MXKRoomMemberDetailsActionUnignore:
|
|
title = [VectorL10n roomParticipantsActionUnignore];
|
|
break;
|
|
case MXKRoomMemberDetailsActionSetDefaultPowerLevel:
|
|
title = [VectorL10n roomParticipantsActionSetDefaultPowerLevel];
|
|
break;
|
|
case MXKRoomMemberDetailsActionSetModerator:
|
|
title = [VectorL10n roomParticipantsActionSetModerator];
|
|
break;
|
|
case MXKRoomMemberDetailsActionSetAdmin:
|
|
title = [VectorL10n roomParticipantsActionSetAdmin];
|
|
break;
|
|
case MXKRoomMemberDetailsActionStartChat:
|
|
title = [VectorL10n roomParticipantsActionStartNewChat];
|
|
break;
|
|
case MXKRoomMemberDetailsActionStartVoiceCall:
|
|
title = [VectorL10n roomParticipantsActionStartVoiceCall];
|
|
break;
|
|
case MXKRoomMemberDetailsActionStartVideoCall:
|
|
title = [VectorL10n roomParticipantsActionStartVideoCall];
|
|
break;
|
|
case MXKRoomMemberDetailsActionMention:
|
|
title = [VectorL10n roomParticipantsActionMention];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return title;
|
|
}
|
|
|
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
UITableViewCell *cell;
|
|
|
|
if (indexPath.section == securityIndex && indexPath.row < securityActionsArray.count)
|
|
{
|
|
NSNumber *actionNumber = securityActionsArray[indexPath.row];
|
|
|
|
if (actionNumber.unsignedIntegerValue == MXKRoomMemberDetailsActionSecurity)
|
|
{
|
|
MXKTableViewCell *securityStatusCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
|
|
|
|
NSString *statusText;
|
|
|
|
switch (self.encryptionTrustLevel) {
|
|
case UserEncryptionTrustLevelTrusted:
|
|
statusText = [VectorL10n roomParticipantsActionSecurityStatusVerified];
|
|
break;
|
|
case UserEncryptionTrustLevelNotVerified:
|
|
case UserEncryptionTrustLevelNoCrossSigning:
|
|
{
|
|
if (self.isRoomMemberCurrentUser)
|
|
{
|
|
statusText = [VectorL10n roomParticipantsActionSecurityStatusCompleteSecurity];
|
|
}
|
|
else
|
|
{
|
|
statusText = [VectorL10n roomParticipantsActionSecurityStatusVerify];
|
|
}
|
|
}
|
|
break;
|
|
case UserEncryptionTrustLevelWarning:
|
|
statusText = [VectorL10n roomParticipantsActionSecurityStatusWarning];
|
|
break;
|
|
default:
|
|
statusText = [VectorL10n roomParticipantsActionSecurityStatusLoading];
|
|
break;
|
|
}
|
|
|
|
securityStatusCell.imageView.image = [EncryptionTrustLevelBadgeImageHelper userBadgeImageFor:self.encryptionTrustLevel];
|
|
|
|
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 vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
|
|
|
|
cell = securityStatusCell;
|
|
}
|
|
else if (actionNumber.unsignedIntegerValue == MXKRoomMemberDetailsActionSecurityInformation)
|
|
{
|
|
MXKTableViewCell *encryptionInfoCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
|
|
|
|
NSMutableString *encryptionInformation = [NSMutableString new];
|
|
|
|
switch (self.encryptionTrustLevel) {
|
|
case UserEncryptionTrustLevelWarning:
|
|
case UserEncryptionTrustLevelNotVerified:
|
|
case UserEncryptionTrustLevelNoCrossSigning:
|
|
case UserEncryptionTrustLevelTrusted:
|
|
{
|
|
NSString *info = (self.mxRoom.isDirect) ?
|
|
[VectorL10n roomParticipantsSecurityInformationRoomEncryptedForDm] :
|
|
[VectorL10n roomParticipantsSecurityInformationRoomEncrypted];
|
|
[encryptionInformation appendString:info];
|
|
}
|
|
break;
|
|
case UserEncryptionTrustLevelNone:
|
|
{
|
|
NSString *info = (self.mxRoom.isDirect) ?
|
|
[VectorL10n roomParticipantsSecurityInformationRoomNotEncryptedForDm] :
|
|
[VectorL10n roomParticipantsSecurityInformationRoomNotEncrypted];
|
|
[encryptionInformation appendString:info];
|
|
}
|
|
break;
|
|
case UserEncryptionTrustLevelUnknown:
|
|
[encryptionInformation appendString:[VectorL10n roomParticipantsSecurityLoading]];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (encryptionInformation.length)
|
|
{
|
|
[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;
|
|
|
|
// extend background color to safe area
|
|
UIView *bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
|
|
bgView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
|
|
encryptionInfoCell.backgroundView = bgView;
|
|
|
|
cell = encryptionInfoCell;
|
|
}
|
|
}
|
|
else if (indexPath.section == adminToolsIndex || indexPath.section == otherActionsIndex)
|
|
{
|
|
TableViewCellWithButton *cellWithButton = [tableView dequeueReusableCellWithIdentifier:[TableViewCellWithButton defaultReuseIdentifier] forIndexPath:indexPath];
|
|
|
|
NSNumber *actionNumber;
|
|
if (indexPath.section == adminToolsIndex && indexPath.row < adminActionsArray.count)
|
|
{
|
|
actionNumber = adminActionsArray[indexPath.row];
|
|
}
|
|
else if (indexPath.section == otherActionsIndex && indexPath.row < otherActionsArray.count)
|
|
{
|
|
actionNumber = otherActionsArray[indexPath.row];
|
|
}
|
|
|
|
if (actionNumber)
|
|
{
|
|
NSString *title = [self actionButtonTitle:actionNumber.unsignedIntegerValue];
|
|
|
|
[cellWithButton.mxkButton setTitle:title forState:UIControlStateNormal];
|
|
[cellWithButton.mxkButton setTitle:title forState:UIControlStateHighlighted];
|
|
|
|
if (actionNumber.unsignedIntegerValue == MXKRoomMemberDetailsActionKick)
|
|
{
|
|
[cellWithButton.mxkButton setTitleColor:ThemeService.shared.theme.warningColor forState:UIControlStateNormal];
|
|
[cellWithButton.mxkButton setTitleColor:ThemeService.shared.theme.warningColor forState:UIControlStateHighlighted];
|
|
}
|
|
else
|
|
{
|
|
[cellWithButton.mxkButton setTitleColor:ThemeService.shared.theme.textPrimaryColor forState:UIControlStateNormal];
|
|
[cellWithButton.mxkButton setTitleColor:ThemeService.shared.theme.textPrimaryColor forState:UIControlStateHighlighted];
|
|
}
|
|
|
|
[cellWithButton.mxkButton addTarget:self action:@selector(onActionButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
|
cellWithButton.mxkButton.tag = actionNumber.unsignedIntegerValue;
|
|
}
|
|
|
|
cell = cellWithButton;
|
|
}
|
|
else if (indexPath.section == directChatsIndex)
|
|
{
|
|
RoomTableViewCell *roomCell = [tableView dequeueReusableCellWithIdentifier:[RoomTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
|
|
|
|
if (indexPath.row < directChatsArray.count)
|
|
{
|
|
MXRoom *room = [self.mainSession roomWithRoomId:directChatsArray[indexPath.row]];
|
|
if (room)
|
|
{
|
|
[roomCell render:room];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
roomCell.avatarImageView.image = AssetImages.startChat.image;
|
|
roomCell.avatarImageView.defaultBackgroundColor = [UIColor clearColor];
|
|
roomCell.avatarImageView.userInteractionEnabled = NO;
|
|
roomCell.titleLabel.text = [VectorL10n roomParticipantsActionStartNewChat];
|
|
}
|
|
|
|
cell = roomCell;
|
|
}
|
|
else if (indexPath.section == devicesIndex)
|
|
{
|
|
DeviceTableViewCell *deviceCell = [tableView dequeueReusableCellWithIdentifier:[DeviceTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
|
|
deviceCell.selectionStyle = UITableViewCellSelectionStyleNone;
|
|
|
|
if (indexPath.row < devicesArray.count)
|
|
{
|
|
MXDeviceInfo *deviceInfo = devicesArray[indexPath.row];
|
|
[deviceCell render:deviceInfo];
|
|
deviceCell.delegate = self;
|
|
|
|
// Display here the Verify and Block buttons except if the device is the current one.
|
|
deviceCell.verifyButton.hidden = deviceCell.blockButton.hidden = [deviceInfo.deviceId isEqualToString:self.mxRoom.mxSession.matrixRestClient.credentials.deviceId];
|
|
}
|
|
cell = deviceCell;
|
|
}
|
|
else
|
|
{
|
|
// Create a fake cell to prevent app from crashing
|
|
cell = [[UITableViewCell alloc] init];
|
|
}
|
|
|
|
return cell;
|
|
}
|
|
|
|
#pragma mark - UITableView delegate
|
|
|
|
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
|
|
{
|
|
cell.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
|
|
|
// Update the selected background view
|
|
if (ThemeService.shared.theme.selectedBackgroundColor)
|
|
{
|
|
cell.selectedBackgroundView = [[UIView alloc] init];
|
|
cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor;
|
|
}
|
|
else
|
|
{
|
|
if (tableView.style == UITableViewStylePlain)
|
|
{
|
|
cell.selectedBackgroundView = nil;
|
|
}
|
|
else
|
|
{
|
|
cell.selectedBackgroundView.backgroundColor = nil;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
|
{
|
|
return TABLEVIEW_SECTION_HEADER_HEIGHT;
|
|
}
|
|
|
|
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
|
{
|
|
if (indexPath.section == securityIndex)
|
|
{
|
|
if (self.encryptionTrustLevel == UserEncryptionTrustLevelNotVerified)
|
|
{
|
|
if (self.isRoomMemberCurrentUser)
|
|
{
|
|
[self presentCompleteSecurity];
|
|
}
|
|
else
|
|
{
|
|
[self startUserVerification];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
[self presentUserVerification];
|
|
}
|
|
}
|
|
else if (indexPath.section == directChatsIndex)
|
|
{
|
|
if (indexPath.row < directChatsArray.count)
|
|
{
|
|
// Open this room
|
|
[self showRoomWithId:directChatsArray[indexPath.row]];
|
|
}
|
|
else
|
|
{
|
|
// Create a new direct chat with the member
|
|
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
button.tag = MXKRoomMemberDetailsActionStartChat;
|
|
|
|
[super onActionButtonPressed:button];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
|
|
if (selectedCell && [selectedCell isKindOfClass:TableViewCellWithButton.class])
|
|
{
|
|
TableViewCellWithButton *cell = (TableViewCellWithButton*)selectedCell;
|
|
|
|
[self onActionButtonPressed:cell.mxkButton];
|
|
}
|
|
}
|
|
|
|
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
|
}
|
|
|
|
#pragma mark - Action
|
|
|
|
- (void)onActionButtonPressed:(id)sender
|
|
{
|
|
if ([sender isKindOfClass:[UIButton class]])
|
|
{
|
|
// already a pending action
|
|
if ([self hasPendingAction])
|
|
{
|
|
return;
|
|
}
|
|
|
|
UIButton *button = (UIButton*)sender;
|
|
|
|
switch (button.tag)
|
|
{
|
|
case MXKRoomMemberDetailsActionSetDefaultPowerLevel:
|
|
{
|
|
[self.mxRoom state:^(MXRoomState *roomState) {
|
|
[self setPowerLevel:roomState.powerLevels.usersDefault promptUser:YES];
|
|
}];
|
|
break;
|
|
}
|
|
case MXKRoomMemberDetailsActionSetModerator:
|
|
{
|
|
[self setPowerLevel:RoomPowerLevelModerator promptUser:YES];
|
|
break;
|
|
}
|
|
case MXKRoomMemberDetailsActionSetAdmin:
|
|
{
|
|
[self setPowerLevel:RoomPowerLevelAdmin promptUser:YES];
|
|
break;
|
|
}
|
|
case MXKRoomMemberDetailsActionBan:
|
|
{
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
// Ban
|
|
currentAlert = [UIAlertController alertControllerWithTitle:[VectorL10n roomEventActionBanPromptReason]
|
|
message:nil
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
[currentAlert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
|
|
textField.secureTextEntry = NO;
|
|
textField.placeholder = nil;
|
|
textField.keyboardType = UIKeyboardTypeDefault;
|
|
}];
|
|
|
|
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
|
|
style:UIAlertActionStyleDefault
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
if (weakSelf)
|
|
{
|
|
typeof(self) self = weakSelf;
|
|
self->currentAlert = nil;
|
|
}
|
|
|
|
}]];
|
|
|
|
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n ban]
|
|
style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
|
|
|
|
if (weakSelf)
|
|
{
|
|
typeof(self) self = weakSelf;
|
|
|
|
NSString *text = [self->currentAlert textFields].firstObject.text;
|
|
|
|
self->currentAlert = nil;
|
|
|
|
[self startActivityIndicator];
|
|
|
|
// kick user
|
|
[self.mxRoom banUser:self.mxRoomMember.userId reason:text success:^{
|
|
|
|
__strong __typeof(weakSelf)self = weakSelf;
|
|
[self stopActivityIndicator];
|
|
|
|
} failure:^(NSError *error) {
|
|
|
|
__strong __typeof(weakSelf)self = weakSelf;
|
|
[self stopActivityIndicator];
|
|
|
|
MXLogDebug(@"[RoomMemberDetailVC] Ban user (%@) failed", self.mxRoomMember.userId);
|
|
//Alert user
|
|
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
|
|
|
}];
|
|
}
|
|
|
|
}]];
|
|
|
|
[currentAlert mxk_setAccessibilityIdentifier:@"RoomMemberDetailsVCBanAlert"];
|
|
[self presentViewController:currentAlert animated:YES completion:nil];
|
|
break;
|
|
}
|
|
case MXKRoomMemberDetailsActionKick:
|
|
{
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
// Kick
|
|
currentAlert = [UIAlertController alertControllerWithTitle:[VectorL10n roomEventActionKickPromptReason]
|
|
message:nil
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
[currentAlert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
|
|
textField.secureTextEntry = NO;
|
|
textField.placeholder = nil;
|
|
textField.keyboardType = UIKeyboardTypeDefault;
|
|
}];
|
|
|
|
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
|
|
style:UIAlertActionStyleDefault
|
|
handler:^(UIAlertAction * action) {
|
|
|
|
if (weakSelf)
|
|
{
|
|
typeof(self) self = weakSelf;
|
|
self->currentAlert = nil;
|
|
}
|
|
|
|
}]];
|
|
|
|
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n remove]
|
|
style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
|
|
|
|
if (weakSelf)
|
|
{
|
|
typeof(self) self = weakSelf;
|
|
|
|
NSString *text = [self->currentAlert textFields].firstObject.text;
|
|
|
|
self->currentAlert = nil;
|
|
|
|
[self startActivityIndicator];
|
|
|
|
// kick user
|
|
[self.mxRoom kickUser:self.mxRoomMember.userId reason:text success:^{
|
|
|
|
__strong __typeof(weakSelf)self = weakSelf;
|
|
[self stopActivityIndicator];
|
|
|
|
} failure:^(NSError *error) {
|
|
|
|
__strong __typeof(weakSelf)self = weakSelf;
|
|
[self stopActivityIndicator];
|
|
|
|
MXLogDebug(@"[RoomMemberDetailVC] Removing user (%@) failed", self.mxRoomMember.userId);
|
|
//Alert user
|
|
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
|
|
|
}];
|
|
}
|
|
|
|
}]];
|
|
|
|
[currentAlert mxk_setAccessibilityIdentifier:@"RoomMemberDetailsVCKickAlert"];
|
|
[self presentViewController:currentAlert animated:YES completion:nil];
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
[super onActionButtonPressed:sender];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)handleTapGesture:(UITapGestureRecognizer*)tapGestureRecognizer
|
|
{
|
|
UIView *view = tapGestureRecognizer.view;
|
|
|
|
if (view == memberTitleView.memberAvatarMask || view == self.roomMemberAvatarMask)
|
|
{
|
|
MXWeakify(self);
|
|
|
|
// Show the avatar in full screen
|
|
__block MXKImageView * avatarFullScreenView = [[MXKImageView alloc] initWithFrame:CGRectZero];
|
|
avatarFullScreenView.stretchable = YES;
|
|
|
|
[avatarFullScreenView setRightButtonTitle:[VectorL10n ok]
|
|
handler:^(MXKImageView* imageView, NSString* buttonTitle) {
|
|
|
|
MXStrongifyAndReturnIfNil(self);
|
|
[avatarFullScreenView dismissSelection];
|
|
[avatarFullScreenView removeFromSuperview];
|
|
|
|
avatarFullScreenView = nil;
|
|
|
|
// Restore the status bar
|
|
self->isStatusBarHidden = NO;
|
|
[self setNeedsStatusBarAppearanceUpdate];
|
|
}];
|
|
|
|
[avatarFullScreenView setImageURI:self.mxRoomMember.avatarUrl
|
|
withType:nil
|
|
andImageOrientation:UIImageOrientationUp
|
|
previewImage:self.memberThumbnail.image
|
|
mediaManager:self.mainSession.mediaManager];
|
|
|
|
[avatarFullScreenView showFullScreen];
|
|
|
|
// Hide the status bar
|
|
isStatusBarHidden = YES;
|
|
// Trigger status bar update
|
|
[self setNeedsStatusBarAppearanceUpdate];
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)deviceTableViewCell:(DeviceTableViewCell*)deviceTableViewCell updateDeviceVerification:(MXDeviceVerification)verificationStatus
|
|
{
|
|
if (verificationStatus == MXDeviceVerified)
|
|
{
|
|
keyVerificationCoordinatorBridgePresenter = [[KeyVerificationCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
|
|
keyVerificationCoordinatorBridgePresenter.delegate = self;
|
|
|
|
[keyVerificationCoordinatorBridgePresenter presentFrom:self otherUserId:deviceTableViewCell.deviceInfo.userId otherDeviceId:deviceTableViewCell.deviceInfo.deviceId animated:YES];
|
|
}
|
|
else
|
|
{
|
|
[self.mxRoom.mxSession.crypto setDeviceVerification:verificationStatus
|
|
forDevice:deviceTableViewCell.deviceInfo.deviceId
|
|
ofUser:self.mxRoomMember.userId
|
|
success:^{
|
|
[self updateMemberInfo];
|
|
} failure:nil];
|
|
}
|
|
}
|
|
|
|
#pragma mark - RoomMemberTitleViewDelegate
|
|
|
|
- (void)roomMemberTitleViewDidLayoutSubview:(RoomMemberTitleView*)titleView
|
|
{
|
|
[self viewDidLayoutSubviews];
|
|
}
|
|
|
|
#pragma mark - KeyVerificationCoordinatorBridgePresenterDelegate
|
|
|
|
- (void)keyVerificationCoordinatorBridgePresenterDelegateDidComplete:(KeyVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId
|
|
{
|
|
[self refreshUserEncryptionTrustLevel];
|
|
[self dismissKeyVerificationCoordinatorBridgePresenter];
|
|
}
|
|
|
|
- (void)keyVerificationCoordinatorBridgePresenterDelegateDidCancel:(KeyVerificationCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter
|
|
{
|
|
[self dismissKeyVerificationCoordinatorBridgePresenter];
|
|
}
|
|
|
|
- (void)dismissKeyVerificationCoordinatorBridgePresenter
|
|
{
|
|
[keyVerificationCoordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
|
keyVerificationCoordinatorBridgePresenter = nil;
|
|
}
|
|
|
|
#pragma mark - UserVerificationCoordinatorBridgePresenterDelegate
|
|
|
|
- (void)userVerificationCoordinatorBridgePresenterDelegateDidComplete:(UserVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter
|
|
{
|
|
[self refreshUserEncryptionTrustLevel];
|
|
}
|
|
|
|
@end
|