element-ios/Riot/Modules/MatrixKit/Controllers/MXKRoomMemberDetailsViewCon...

1029 lines
42 KiB
Objective-C

/*
Copyright 2018-2024 New Vector Ltd.
Copyright 2017 Vector Creations Ltd
Copyright 2015 OpenMarket Ltd
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
#import "MXKRoomMemberDetailsViewController.h"
@import MatrixSDK.MXMediaManager;
#import "MXKTableViewCellWithButtons.h"
#import "NSBundle+MatrixKit.h"
#import "MXKAppSettings.h"
#import "MXKConstants.h"
#import "MXKSwiftHeader.h"
@interface MXKRoomMemberDetailsViewController ()
{
id membersListener;
// mask view while processing a request
UIActivityIndicatorView * pendingMaskSpinnerView;
// Observe left rooms
id leaveRoomNotificationObserver;
// Observe kMXRoomDidFlushDataNotification to take into account the updated room members when the room history is flushed.
id roomDidFlushDataNotificationObserver;
// Cache for the room live timeline
id<MXEventTimeline> mxRoomLiveTimeline;
}
@end
@implementation MXKRoomMemberDetailsViewController
@synthesize mxRoom;
+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass([MXKRoomMemberDetailsViewController class])
bundle:[NSBundle bundleForClass:[MXKRoomMemberDetailsViewController class]]];
}
+ (instancetype)roomMemberDetailsViewController
{
return [[[self class] alloc] initWithNibName:NSStringFromClass([MXKRoomMemberDetailsViewController class])
bundle:[NSBundle bundleForClass:[MXKRoomMemberDetailsViewController class]]];
}
- (void)finalizeInit
{
[super finalizeInit];
actionsArray = [[NSMutableArray alloc] init];
_enableLeave = YES;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Check whether the view controller has been pushed via storyboard
if (!self.tableView)
{
// Instantiate view controller objects
[[[self class] nib] instantiateWithOwner:self options:nil];
}
// ignore useless update
if (_mxRoomMember)
{
[self initObservers];
}
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self initObservers];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self removeObservers];
}
- (void)destroy
{
// close any pending actionsheet
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
currentAlert = nil;
}
[self removePendingActionMask];
[self removeObservers];
_delegate = nil;
_mxRoomMember = nil;
actionsArray = nil;
[super destroy];
}
#pragma mark -
- (void)displayRoomMember:(MXRoomMember*)roomMember withMatrixRoom:(MXRoom*)room
{
[self removeObservers];
mxRoom = room;
MXWeakify(self);
[mxRoom liveTimeline:^(id<MXEventTimeline> liveTimeline) {
MXStrongifyAndReturnIfNil(self);
self->mxRoomLiveTimeline = liveTimeline;
// Update matrix session associated to the view controller
NSArray *mxSessions = self.mxSessions;
for (MXSession *mxSession in mxSessions) {
[self removeMatrixSession:mxSession];
}
[self addMatrixSession:room.mxSession];
self->_mxRoomMember = roomMember;
[self initObservers];
}];
}
- (id<MXEventTimeline> )mxRoomLiveTimeline
{
// @TODO(async-state): Just here for dev
NSAssert(mxRoomLiveTimeline, @"[MXKRoomMemberDetailsViewController] Room live timeline must be preloaded before accessing to MXKRoomMemberDetailsViewController.mxRoomLiveTimeline");
return mxRoomLiveTimeline;
}
- (UIImage*)picturePlaceholder
{
return [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"default-profile"];
}
- (void)setEnableMention:(BOOL)enableMention
{
if (_enableMention != enableMention)
{
_enableMention = enableMention;
[self updateMemberInfo];
}
}
- (void)setEnableVoipCall:(BOOL)enableVoipCall
{
if (_enableVoipCall != enableVoipCall)
{
_enableVoipCall = enableVoipCall;
[self updateMemberInfo];
}
}
- (void)setEnableLeave:(BOOL)enableLeave
{
if (_enableLeave != enableLeave)
{
_enableLeave = enableLeave;
[self updateMemberInfo];
}
}
- (IBAction)onActionButtonPressed:(id)sender
{
if ([sender isKindOfClass:[UIButton class]])
{
// Check whether an action is already in progress
if ([self hasPendingAction])
{
return;
}
UIButton *button = (UIButton*)sender;
switch (button.tag)
{
case MXKRoomMemberDetailsActionInvite:
{
[self addPendingActionMask];
[mxRoom inviteUser:_mxRoomMember.userId
success:^{
[self removePendingActionMask];
} failure:^(NSError *error) {
[self removePendingActionMask];
MXLogDebug(@"[MXKRoomMemberDetailsVC] Invite %@ failed", self->_mxRoomMember.userId);
// Notify MatrixKit user
NSString *myUserId = self.mainSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
break;
}
case MXKRoomMemberDetailsActionLeave:
{
[self addPendingActionMask];
[self.mxRoom leave:^{
[self removePendingActionMask];
[self withdrawViewControllerAnimated:YES completion:nil];
} failure:^(NSError *error) {
[self removePendingActionMask];
MXLogDebug(@"[MXKRoomMemberDetailsVC] Leave room %@ failed", self->mxRoom.roomId);
// Notify MatrixKit user
NSString *myUserId = self.mainSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
break;
}
case MXKRoomMemberDetailsActionKick:
{
[self addPendingActionMask];
[mxRoom kickUser:_mxRoomMember.userId
reason:nil
success:^{
[self removePendingActionMask];
// Pop/Dismiss the current view controller if the left members are hidden
if (![[MXKAppSettings standardAppSettings] showLeftMembersInRoomMemberList])
{
[self withdrawViewControllerAnimated:YES completion:nil];
}
} failure:^(NSError *error) {
[self removePendingActionMask];
MXLogDebug(@"[MXKRoomMemberDetailsVC] Kick %@ failed", self->_mxRoomMember.userId);
// Notify MatrixKit user
NSString *myUserId = self.mainSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
break;
}
case MXKRoomMemberDetailsActionBan:
{
[self addPendingActionMask];
[mxRoom banUser:_mxRoomMember.userId
reason:nil
success:^{
[self removePendingActionMask];
} failure:^(NSError *error) {
[self removePendingActionMask];
MXLogDebug(@"[MXKRoomMemberDetailsVC] Ban %@ failed", self->_mxRoomMember.userId);
// Notify MatrixKit user
NSString *myUserId = self.mainSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
break;
}
case MXKRoomMemberDetailsActionUnban:
{
[self addPendingActionMask];
[mxRoom unbanUser:_mxRoomMember.userId
success:^{
[self removePendingActionMask];
} failure:^(NSError *error) {
[self removePendingActionMask];
MXLogDebug(@"[MXKRoomMemberDetailsVC] Unban %@ failed", self->_mxRoomMember.userId);
// Notify MatrixKit user
NSString *myUserId = self.mainSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
break;
}
case MXKRoomMemberDetailsActionIgnore:
{
// Prompt user to ignore content from this user
MXWeakify(self);
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
}
currentAlert = [UIAlertController alertControllerWithTitle:[VectorL10n roomMemberIgnorePrompt] message:nil preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n yes]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
// Add the user to the blacklist: ignored users
[self addPendingActionMask];
MXWeakify(self);
[self.mainSession ignoreUsers:@[self.mxRoomMember.userId]
success:^{
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
MXLogDebug(@"[MXKRoomMemberDetailsVC] Ignore %@ failed", self.mxRoomMember.userId);
// Notify MatrixKit user
NSString *myUserId = self.mainSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
}]];
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n no]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
}]];
[self presentViewController:currentAlert animated:YES completion:nil];
break;
}
case MXKRoomMemberDetailsActionUnignore:
{
// Remove the member from the ignored user list.
[self addPendingActionMask];
MXWeakify(self);
[self.mainSession unIgnoreUsers:@[self.mxRoomMember.userId]
success:^{
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
[self removePendingActionMask];
MXLogDebug(@"[MXKRoomMemberDetailsVC] Unignore %@ failed", self.mxRoomMember.userId);
// Notify MatrixKit user
NSString *myUserId = self.mainSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
break;
}
case MXKRoomMemberDetailsActionSetDefaultPowerLevel:
{
break;
}
case MXKRoomMemberDetailsActionSetModerator:
{
break;
}
case MXKRoomMemberDetailsActionSetAdmin:
{
break;
}
case MXKRoomMemberDetailsActionSetCustomPowerLevel:
{
[self updateUserPowerLevel];
break;
}
case MXKRoomMemberDetailsActionStartChat:
{
if (self.delegate)
{
[self addPendingActionMask];
[self.delegate roomMemberDetailsViewController:self startChatWithMemberId:_mxRoomMember.userId completion:^{
[self removePendingActionMask];
}];
}
break;
}
case MXKRoomMemberDetailsActionStartVoiceCall:
case MXKRoomMemberDetailsActionStartVideoCall:
{
BOOL isVideoCall = (button.tag == MXKRoomMemberDetailsActionStartVideoCall);
if (self.delegate && [self.delegate respondsToSelector:@selector(roomMemberDetailsViewController:placeVoipCallWithMemberId:andVideo:)])
{
[self addPendingActionMask];
[self.delegate roomMemberDetailsViewController:self placeVoipCallWithMemberId:_mxRoomMember.userId andVideo:isVideoCall];
[self removePendingActionMask];
}
else
{
[self addPendingActionMask];
MXRoom* directRoom = [self.mainSession directJoinedRoomWithUserId:_mxRoomMember.userId];
// Place the call directly if the room exists
if (directRoom)
{
[directRoom placeCallWithVideo:isVideoCall success:nil failure:nil];
[self removePendingActionMask];
}
else
{
// Create a new room
MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters parametersForDirectRoomWithUser:_mxRoomMember.userId];
[self.mainSession createRoomWithParameters:roomCreationParameters success:^(MXRoom *room) {
// Delay the call in order to be sure that the room is ready
dispatch_async(dispatch_get_main_queue(), ^{
[room placeCallWithVideo:isVideoCall success:nil failure:nil];
[self removePendingActionMask];
});
} failure:^(NSError *error) {
MXLogDebug(@"[MXKRoomMemberDetailsVC] Create room failed");
[self removePendingActionMask];
// Notify MatrixKit user
NSString *myUserId = self.mainSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
}
}
break;
}
case MXKRoomMemberDetailsActionMention:
{
// Sanity check
if (_delegate && [_delegate respondsToSelector:@selector(roomMemberDetailsViewController:mention:)])
{
id<MXKRoomMemberDetailsViewControllerDelegate> delegate = _delegate;
MXRoomMember *member = _mxRoomMember;
// Withdraw the current view controller, and let the delegate mention the member
[self withdrawViewControllerAnimated:YES completion:^{
[delegate roomMemberDetailsViewController:self mention:member];
}];
}
break;
}
default:
break;
}
}
}
#pragma mark - Internals
- (void)initObservers
{
// Remove any pending observers
[self removeObservers];
if (mxRoom)
{
// Observe room's members update
NSArray *mxMembersEvents = @[kMXEventTypeStringRoomMember, kMXEventTypeStringRoomPowerLevels];
self->membersListener = [mxRoom listenToEventsOfTypes:mxMembersEvents onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) {
// consider only live event
if (direction == MXTimelineDirectionForwards)
{
[self refreshRoomMember];
}
}];
// Observe kMXSessionWillLeaveRoomNotification to be notified if the user leaves the current room.
leaveRoomNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionWillLeaveRoomNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
// Check whether the user will leave the room related to the displayed member
if (notif.object == self.mainSession)
{
NSString *roomId = notif.userInfo[kMXSessionNotificationRoomIdKey];
if (roomId && [roomId isEqualToString:self->mxRoom.roomId])
{
// We must remove the current view controller.
[self withdrawViewControllerAnimated:YES completion:nil];
}
}
}];
// Observe room history flush (sync with limited timeline, or state event redaction)
roomDidFlushDataNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXRoomDidFlushDataNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXRoom *room = notif.object;
if (self.mainSession == room.mxSession && [self->mxRoom.roomId isEqualToString:room.roomId])
{
// The existing room history has been flushed during server sync.
// Take into account the updated room members list by updating the room member instance
[self refreshRoomMember];
}
}];
}
[self updateMemberInfo];
}
- (void)removeObservers
{
if (leaveRoomNotificationObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:leaveRoomNotificationObserver];
leaveRoomNotificationObserver = nil;
}
if (roomDidFlushDataNotificationObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:roomDidFlushDataNotificationObserver];
roomDidFlushDataNotificationObserver = nil;
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (membersListener && mxRoom)
{
MXWeakify(self);
[mxRoom liveTimeline:^(id<MXEventTimeline> liveTimeline) {
MXStrongifyAndReturnIfNil(self);
[liveTimeline removeListener:self->membersListener];
self->membersListener = nil;
}];
}
}
- (void)refreshRoomMember
{
// Hide potential action sheet
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
currentAlert = nil;
}
MXRoomMember* nextRoomMember = nil;
// get the updated memmber
NSArray<MXRoomMember *> *membersList = self.mxRoomLiveTimeline.state.members.members;
for (MXRoomMember* member in membersList)
{
if ([member.userId isEqualToString:_mxRoomMember.userId])
{
nextRoomMember = member;
break;
}
}
// does the member still exist ?
if (nextRoomMember)
{
// Refresh member
_mxRoomMember = nextRoomMember;
[self updateMemberInfo];
}
else
{
[self withdrawViewControllerAnimated:YES completion:nil];
}
}
- (void)updateMemberInfo
{
self.title = _mxRoomMember.displayname ? _mxRoomMember.displayname : _mxRoomMember.userId;
// set the thumbnail info
self.memberThumbnail.contentMode = UIViewContentModeScaleAspectFill;
self.memberThumbnail.defaultBackgroundColor = [UIColor clearColor];
[self.memberThumbnail.layer setCornerRadius:self.memberThumbnail.frame.size.width / 2];
[self.memberThumbnail setClipsToBounds:YES];
self.memberThumbnail.mediaFolder = kMXMediaManagerAvatarThumbnailFolder;
self.memberThumbnail.enableInMemoryCache = YES;
[self.memberThumbnail setImageURI:_mxRoomMember.avatarUrl
withType:nil
andImageOrientation:UIImageOrientationUp
toFitViewSize:self.memberThumbnail.frame.size
withMethod:MXThumbnailingMethodCrop
previewImage:self.picturePlaceholder
mediaManager:self.mainSession.mediaManager];
self.roomMemberMatrixInfo.text = _mxRoomMember.userId;
[self.tableView reloadData];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Check user's power level before allowing an action (kick, ban, ...)
MXRoomPowerLevels *powerLevels = [self.mxRoomLiveTimeline.state powerLevels];
NSInteger memberPowerLevel = [powerLevels powerLevelOfUserWithUserID:_mxRoomMember.userId];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
[actionsArray removeAllObjects];
// Consider the case of the user himself
if ([_mxRoomMember.userId isEqualToString:self.mainSession.myUser.userId])
{
if (_enableLeave)
{
[actionsArray addObject:@(MXKRoomMemberDetailsActionLeave)];
}
if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomPowerLevels])
{
[actionsArray addObject:@(MXKRoomMemberDetailsActionSetCustomPowerLevel)];
}
}
else if (_mxRoomMember)
{
if (_enableVoipCall)
{
// Offer voip call options
[actionsArray addObject:@(MXKRoomMemberDetailsActionStartVoiceCall)];
[actionsArray addObject:@(MXKRoomMemberDetailsActionStartVideoCall)];
}
// Consider membership of the selected member
switch (_mxRoomMember.membership)
{
case MXMembershipInvite:
case MXMembershipJoin:
{
// Check conditions to be able to kick someone
if (oneSelfPowerLevel >= [powerLevels kick] && oneSelfPowerLevel > memberPowerLevel)
{
[actionsArray addObject:@(MXKRoomMemberDetailsActionKick)];
}
// Check conditions to be able to ban someone
if (oneSelfPowerLevel >= [powerLevels ban] && oneSelfPowerLevel > memberPowerLevel)
{
[actionsArray addObject:@(MXKRoomMemberDetailsActionBan)];
}
// Check whether the option Ignore may be presented
if (_mxRoomMember.membership == MXMembershipJoin)
{
// is he already ignored ?
if (![self.mainSession isUserIgnored:_mxRoomMember.userId])
{
[actionsArray addObject:@(MXKRoomMemberDetailsActionIgnore)];
}
else
{
[actionsArray addObject:@(MXKRoomMemberDetailsActionUnignore)];
}
}
break;
}
case MXMembershipLeave:
{
// Check conditions to be able to invite someone
if (oneSelfPowerLevel >= [powerLevels invite])
{
[actionsArray addObject:@(MXKRoomMemberDetailsActionInvite)];
}
// Check conditions to be able to ban someone
if (oneSelfPowerLevel >= [powerLevels ban] && oneSelfPowerLevel > memberPowerLevel)
{
[actionsArray addObject:@(MXKRoomMemberDetailsActionBan)];
}
break;
}
case MXMembershipBan:
{
// Check conditions to be able to unban someone
if (oneSelfPowerLevel >= [powerLevels ban] && oneSelfPowerLevel > memberPowerLevel)
{
[actionsArray addObject:@(MXKRoomMemberDetailsActionUnban)];
}
break;
}
default:
{
break;
}
}
// update power level
if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomPowerLevels] && oneSelfPowerLevel > memberPowerLevel)
{
[actionsArray addObject:@(MXKRoomMemberDetailsActionSetCustomPowerLevel)];
}
// offer to start a new chat only if the room is not the first direct chat with this user
// it does not make sense : it would open the same room
MXRoom* directRoom = [self.mainSession directJoinedRoomWithUserId:_mxRoomMember.userId];
if (!directRoom || (![directRoom.roomId isEqualToString:mxRoom.roomId]))
{
[actionsArray addObject:@(MXKRoomMemberDetailsActionStartChat)];
}
}
if (_enableMention)
{
// Add mention option
[actionsArray addObject:@(MXKRoomMemberDetailsActionMention)];
}
return (actionsArray.count + 1) / 2;
}
- (NSString*)actionButtonTitle:(MXKRoomMemberDetailsAction)action
{
NSString *title;
switch (action)
{
case MXKRoomMemberDetailsActionInvite:
title = [VectorL10n invite];
break;
case MXKRoomMemberDetailsActionLeave:
title = [VectorL10n leave];
break;
case MXKRoomMemberDetailsActionKick:
title = [VectorL10n kick];
break;
case MXKRoomMemberDetailsActionBan:
title = [VectorL10n ban];
break;
case MXKRoomMemberDetailsActionUnban:
title = [VectorL10n unban];
break;
case MXKRoomMemberDetailsActionIgnore:
title = [VectorL10n ignore];
break;
case MXKRoomMemberDetailsActionUnignore:
title = [VectorL10n unignore];
break;
case MXKRoomMemberDetailsActionSetDefaultPowerLevel:
title = [VectorL10n setDefaultPowerLevel];
break;
case MXKRoomMemberDetailsActionSetModerator:
title = [VectorL10n setModerator];
break;
case MXKRoomMemberDetailsActionSetAdmin:
title = [VectorL10n setAdmin];
break;
case MXKRoomMemberDetailsActionSetCustomPowerLevel:
title = [VectorL10n setPowerLevel];
break;
case MXKRoomMemberDetailsActionStartChat:
title = [VectorL10n startChat];
break;
case MXKRoomMemberDetailsActionStartVoiceCall:
title = [VectorL10n startVoiceCall];
break;
case MXKRoomMemberDetailsActionStartVideoCall:
title = [VectorL10n startVideoCall];
break;
case MXKRoomMemberDetailsActionMention:
title = [VectorL10n mention];
break;
default:
break;
}
return title;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.tableView == tableView)
{
NSInteger row = indexPath.row;
MXKTableViewCellWithButtons *cell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButtons defaultReuseIdentifier]];
if (!cell)
{
cell = [[MXKTableViewCellWithButtons alloc] init];
}
cell.mxkButtonNumber = 2;
NSArray *buttons = cell.mxkButtons;
NSInteger index = row * 2;
NSString *text = nil;
for (UIButton *button in buttons)
{
NSNumber *actionNumber;
if (index < actionsArray.count)
{
actionNumber = [actionsArray objectAtIndex:index];
}
text = (actionNumber ? [self actionButtonTitle:actionNumber.unsignedIntegerValue] : nil);
button.hidden = (text.length == 0);
button.layer.borderColor = button.tintColor.CGColor;
button.layer.borderWidth = 1;
button.layer.cornerRadius = 5;
[button setTitle:text forState:UIControlStateNormal];
[button setTitle:text forState:UIControlStateHighlighted];
[button addTarget:self action:@selector(onActionButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
button.tag = (actionNumber ? actionNumber.unsignedIntegerValue : -1);
index ++;
}
return cell;
}
// Return a fake cell to prevent app from crashing.
return [[UITableViewCell alloc] init];
}
#pragma mark - button management
- (BOOL)hasPendingAction
{
return nil != pendingMaskSpinnerView;
}
- (void)addPendingActionMask
{
// add a spinner above the tableview to avoid that the user tap on any other button
pendingMaskSpinnerView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
pendingMaskSpinnerView.backgroundColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.5];
pendingMaskSpinnerView.frame = self.tableView.frame;
pendingMaskSpinnerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
// append it
[self.tableView.superview addSubview:pendingMaskSpinnerView];
// animate it
[pendingMaskSpinnerView startAnimating];
}
- (void)removePendingActionMask
{
if (pendingMaskSpinnerView)
{
[pendingMaskSpinnerView removeFromSuperview];
pendingMaskSpinnerView = nil;
[self.tableView reloadData];
}
}
- (void)setPowerLevel:(NSInteger)value promptUser:(BOOL)promptUser
{
NSInteger currentPowerLevel = [self.mxRoomLiveTimeline.state.powerLevels powerLevelOfUserWithUserID:_mxRoomMember.userId];
// check if the power level has not yet been set to 0
if (value != currentPowerLevel)
{
__weak typeof(self) weakSelf = self;
if (promptUser && value == [self.mxRoomLiveTimeline.state.powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId])
{
// If the user is setting the same power level as his to another user, ask him for a confirmation
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
}
currentAlert = [UIAlertController alertControllerWithTitle:[VectorL10n roomMemberPowerLevelPrompt] message:nil preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n no]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n yes]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
// The user confirms. Apply the power level
[self setPowerLevel:value promptUser:NO];
}
}]];
[self presentViewController:currentAlert animated:YES completion:nil];
}
else
{
[self addPendingActionMask];
// Reset user power level
[self.mxRoom setPowerLevelOfUserWithUserID:_mxRoomMember.userId powerLevel:value success:^{
__strong __typeof(weakSelf)strongSelf = weakSelf;
[strongSelf removePendingActionMask];
} failure:^(NSError *error) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
[strongSelf removePendingActionMask];
MXLogDebug(@"[MXKRoomMemberDetailsVC] Set user power (%@) failed", strongSelf.mxRoomMember.userId);
// Notify MatrixKit user
NSString *myUserId = strongSelf.mainSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
}
}
}
- (void)updateUserPowerLevel
{
__weak typeof(self) weakSelf = self;
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
}
currentAlert = [UIAlertController alertControllerWithTitle:[VectorL10n powerLevel] message:nil preferredStyle:UIAlertControllerStyleAlert];
if (![self.mainSession.myUser.userId isEqualToString:_mxRoomMember.userId])
{
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n resetToDefault]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
[self setPowerLevel:self.mxRoomLiveTimeline.state.powerLevels.usersDefault promptUser:YES];
}
}]];
}
[currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField)
{
typeof(self) self = weakSelf;
textField.secureTextEntry = NO;
textField.text = [NSString stringWithFormat:@"%ld", (long)[self.mxRoomLiveTimeline.state.powerLevels powerLevelOfUserWithUserID:self.mxRoomMember.userId]];
textField.placeholder = nil;
textField.keyboardType = UIKeyboardTypeDecimalPad;
}];
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n ok]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
NSString *text = [self->currentAlert textFields].firstObject.text;
self->currentAlert = nil;
if (text.length > 0)
{
[self setPowerLevel:[text integerValue] promptUser:YES];
}
}
}]];
[self presentViewController:currentAlert animated:YES completion:nil];
}
@end