mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-16 06:28:27 +02:00
Merge pull request #7955 from element-hq/mauroromito/last_owner_can_leave
Last owner can't leave
This commit is contained in:
@@ -479,6 +479,7 @@ Tap the + to start adding people.";
|
||||
"room_participants_invite_malformed_id" = "Malformed ID. Should be an email address or a Matrix ID like '@localpart:domain'";
|
||||
"room_participants_invited_section" = "INVITED";
|
||||
"room_participants_start_new_chat_error_using_user_email_without_identity_server" = "No identity server is configured so you cannot start a chat with a contact using an email.";
|
||||
"room_participants_leave_not_allowed_for_last_owner_msg" = "You can't leave the room since you're the only owner of it.";
|
||||
|
||||
"room_participants_online" = "Online";
|
||||
"room_participants_offline" = "Offline";
|
||||
|
||||
36
Riot/Categories/MXRoom.swift
Normal file
36
Riot/Categories/MXRoom.swift
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Copyright 2025 New Vector Ltd
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
@objc
|
||||
extension MXRoom {
|
||||
/// Returns true if the user is the last owner of the room, but not the last member.
|
||||
func isLastOwner() async throws -> Bool {
|
||||
let userID = mxSession.myUserId
|
||||
let state = try await state()
|
||||
|
||||
let requiredPowerLevel: RoomPowerLevel = state.isMSC4289Supported() ? .owner : .admin
|
||||
|
||||
guard state.powerLevelOfUser(withUserID: userID) >= requiredPowerLevel.rawValue else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard let joinedMembers = try await members()?.members(with: .join) else {
|
||||
return false
|
||||
}
|
||||
|
||||
var areOtherMembers = false
|
||||
for member in joinedMembers where member.userId != userID {
|
||||
// User is not the last member in the whole room.
|
||||
areOtherMembers = true
|
||||
// If there are other owners/admins the user can leave
|
||||
if state.powerLevelOfUser(withUserID: member.userId) >= requiredPowerLevel.rawValue {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return areOtherMembers
|
||||
}
|
||||
}
|
||||
@@ -6447,6 +6447,10 @@ public class VectorL10n: NSObject {
|
||||
public static var roomParticipantsInvitedSection: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_invited_section")
|
||||
}
|
||||
/// You can't leave the room since you're the only owner of it.
|
||||
public static var roomParticipantsLeaveNotAllowedForLastOwnerMsg: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_leave_not_allowed_for_last_owner_msg")
|
||||
}
|
||||
/// Leaving
|
||||
public static var roomParticipantsLeaveProcessing: String {
|
||||
return VectorL10n.tr("Vector", "room_participants_leave_processing")
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
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.
|
||||
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 "RecentsViewController.h"
|
||||
@@ -124,7 +124,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
[tableSearchBar setImage:AssetImages.filterOff.image
|
||||
forSearchBarIcon:UISearchBarIconSearch
|
||||
state:UIControlStateNormal];
|
||||
|
||||
|
||||
tableSearchBar.delegate = self;
|
||||
|
||||
displayedSectionHeaders = [NSMutableArray array];
|
||||
@@ -132,7 +132,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
_contextMenuProvider = [RecentCellContextMenuProvider new];
|
||||
self.contextMenuProvider.serviceDelegate = self;
|
||||
self.contextMenuProvider.menuProviderDelegate = self;
|
||||
|
||||
|
||||
// Set itself as delegate by default.
|
||||
self.delegate = self;
|
||||
}
|
||||
@@ -150,10 +150,10 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
|
||||
// Register key backup banner cells
|
||||
[self.recentsTableView registerNib:SecureBackupBannerCell.nib forCellReuseIdentifier:SecureBackupBannerCell.defaultReuseIdentifier];
|
||||
|
||||
|
||||
// Register key verification banner cells
|
||||
[self.recentsTableView registerNib:CrossSigningSetupBannerCell.nib forCellReuseIdentifier:CrossSigningSetupBannerCell.defaultReuseIdentifier];
|
||||
|
||||
|
||||
[self.recentsTableView registerClass:SectionHeaderView.class
|
||||
forHeaderFooterViewReuseIdentifier:SectionHeaderView.defaultReuseIdentifier];
|
||||
|
||||
@@ -180,7 +180,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
[self.recentsSearchBar setImage:AssetImages.filterOff.image
|
||||
forSearchBarIcon:UISearchBarIconSearch
|
||||
state:UIControlStateNormal];
|
||||
|
||||
|
||||
// Observe user interface theme change.
|
||||
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
@@ -195,7 +195,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
- (void)userInterfaceThemeDidChange
|
||||
{
|
||||
[ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar];
|
||||
|
||||
|
||||
self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor;
|
||||
|
||||
// Use the primary bg color for the recents table view in plain style.
|
||||
@@ -203,15 +203,15 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
self.recentsTableView.separatorColor = ThemeService.shared.theme.lineBreakColor;
|
||||
topview.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
|
||||
self.view.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
|
||||
|
||||
[ThemeService.shared.theme applyStyleOnSearchBar:tableSearchBar];
|
||||
[ThemeService.shared.theme applyStyleOnSearchBar:self.recentsSearchBar];
|
||||
|
||||
|
||||
// Force table refresh
|
||||
[self.recentsTableView reloadData];
|
||||
|
||||
[self.emptyView updateWithTheme:ThemeService.shared.theme];
|
||||
|
||||
|
||||
[self setNeedsStatusBarAppearanceUpdate];
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
isViewVisible = YES;
|
||||
|
||||
[self.screenTracker trackScreen];
|
||||
|
||||
|
||||
// Reset back user interactions
|
||||
self.userInteractionEnabled = YES;
|
||||
|
||||
@@ -341,7 +341,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
// the selected room (if any) is highlighted.
|
||||
[self refreshCurrentSelectedCell:YES];
|
||||
}
|
||||
|
||||
|
||||
if (self.recentsDataSource)
|
||||
{
|
||||
[self refreshRecentsTable];
|
||||
@@ -906,10 +906,10 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
{
|
||||
Analytics.shared.joinedRoomTrigger = AnalyticsJoinedRoomTriggerInvite;
|
||||
}
|
||||
|
||||
|
||||
// Avoid multiple openings of rooms
|
||||
self.userInteractionEnabled = NO;
|
||||
|
||||
|
||||
// Do not stack views when showing room
|
||||
ScreenPresentationParameters *presentationParameters = [[ScreenPresentationParameters alloc] initWithRestoreInitialDisplay:NO stackAboveVisibleViews:NO];
|
||||
|
||||
@@ -928,7 +928,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
- (void)showRoomPreviewWithData:(RoomPreviewData*)roomPreviewData
|
||||
{
|
||||
Analytics.shared.joinedRoomTrigger = AnalyticsJoinedRoomTriggerRoomDirectory;
|
||||
|
||||
|
||||
// Do not stack views when showing room
|
||||
ScreenPresentationParameters *presentationParameters = [[ScreenPresentationParameters alloc] initWithRestoreInitialDisplay:NO stackAboveVisibleViews:NO sender:nil sourceView:nil];
|
||||
|
||||
@@ -1001,7 +1001,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
{
|
||||
// Retrieve the invited room
|
||||
MXRoom *invitedRoom = userInfo[kInviteRecentTableViewCellRoomKey];
|
||||
|
||||
|
||||
if (invitedRoom.summary.roomType == MXRoomTypeSpace)
|
||||
{
|
||||
// Indicates that spaces are not supported
|
||||
@@ -1016,7 +1016,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
{
|
||||
// Retrieve the invited room
|
||||
MXRoom *invitedRoom = userInfo[kInviteRecentTableViewCellRoomKey];
|
||||
|
||||
|
||||
if (invitedRoom.summary.roomType == MXRoomTypeSpace)
|
||||
{
|
||||
// Indicates that spaces are not supported
|
||||
@@ -1055,7 +1055,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
[super dataSource:dataSource didCellChange:changes];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ([changes isKindOfClass:NSIndexPath.class])
|
||||
{
|
||||
NSIndexPath *indexPath = (NSIndexPath *)changes;
|
||||
@@ -1066,7 +1066,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
|
||||
TableViewCellWithCollectionView *collectionViewCell = (TableViewCellWithCollectionView *)cell;
|
||||
[collectionViewCell.collectionView reloadData];
|
||||
|
||||
|
||||
CGRect headerFrame = [self.recentsTableView rectForHeaderInSection:indexPath.section];
|
||||
UIView *headerView = [self.recentsTableView headerViewForSection:indexPath.section];
|
||||
UIView *updatedHeaderView = [self.dataSource viewForHeaderInSection:indexPath.section withFrame:headerFrame inTableView:self.recentsTableView];
|
||||
@@ -1104,7 +1104,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
}
|
||||
|
||||
[self showEmptyViewIfNeeded];
|
||||
|
||||
|
||||
if (dataSource.state == MXKDataSourceStateReady)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RecentsViewControllerDataReadyNotification
|
||||
@@ -1113,109 +1113,120 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
||||
}
|
||||
|
||||
#pragma mark - Swipe actions
|
||||
|
||||
- (void)leaveEditedRoom
|
||||
{
|
||||
if (editedRoomId)
|
||||
{
|
||||
NSString *currentRoomId = editedRoomId;
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
NSString *title, *message;
|
||||
if ([self.mainSession roomWithRoomId:currentRoomId].isDirect)
|
||||
{
|
||||
title = [VectorL10n roomParticipantsLeavePromptTitleForDm];
|
||||
message = [VectorL10n roomParticipantsLeavePromptMsgForDm];
|
||||
}
|
||||
else
|
||||
{
|
||||
title = [VectorL10n roomParticipantsLeavePromptTitle];
|
||||
message = [VectorL10n roomParticipantsLeavePromptMsg];
|
||||
}
|
||||
|
||||
// confirm leave
|
||||
UIAlertController *leavePrompt = [UIAlertController alertControllerWithTitle:title
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[leavePrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->currentAlert = nil;
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[leavePrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n leave]
|
||||
style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->currentAlert = nil;
|
||||
|
||||
// Check whether the user didn't leave the room yet
|
||||
// TODO: Handle multi-account
|
||||
MXRoom *room = [self.mainSession roomWithRoomId:currentRoomId];
|
||||
if (room)
|
||||
{
|
||||
[self startActivityIndicatorWithLabel:[VectorL10n roomParticipantsLeaveProcessing]];
|
||||
// cancel pending uploads/downloads
|
||||
// they are useless by now
|
||||
[MXMediaManager cancelDownloadsInCacheFolder:room.roomId];
|
||||
|
||||
// TODO GFO cancel pending uploads related to this room
|
||||
|
||||
MXLogDebug(@"[RecentsViewController] Leave room (%@)", room.roomId);
|
||||
|
||||
[room leave:^{
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
[self stopActivityIndicator];
|
||||
[self.userIndicatorStore presentSuccessWithLabel:[VectorL10n roomParticipantsLeaveSuccess]];
|
||||
// Force table refresh
|
||||
[self cancelEditionMode:YES];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
MXLogDebug(@"[RecentsViewController] Failed to leave room");
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
// Notify the end user
|
||||
NSString *userId = room.mxSession.myUser.userId;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification
|
||||
object:error
|
||||
userInfo:userId ? @{kMXKErrorUserIdKey: userId} : nil];
|
||||
|
||||
[self stopActivityIndicator];
|
||||
|
||||
// Leave editing mode
|
||||
[self cancelEditionMode:self->isRefreshPending];
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leave editing mode
|
||||
[self cancelEditionMode:self->isRefreshPending];
|
||||
}
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[leavePrompt mxk_setAccessibilityIdentifier:@"LeaveEditedRoomAlert"];
|
||||
[self presentViewController:leavePrompt animated:YES completion:nil];
|
||||
currentAlert = leavePrompt;
|
||||
MXWeakify(self);
|
||||
MXRoom *room = [self.mainSession roomWithRoomId:currentRoomId];
|
||||
__weak typeof(room) weakRoom = room;
|
||||
[room isLastOwnerWithCompletionHandler:^(BOOL isLastOwner, NSError* error){
|
||||
if (isLastOwner)
|
||||
{
|
||||
UIAlertController *isLastOwnerPrompt = [UIAlertController alertControllerWithTitle:[VectorL10n error]
|
||||
message:[VectorL10n roomParticipantsLeaveNotAllowedForLastOwnerMsg]
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[isLastOwnerPrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n ok]
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction * action) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
}]];
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self presentViewController:isLastOwnerPrompt animated:YES completion:nil];
|
||||
self->currentAlert = isLastOwnerPrompt;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *title, *message;
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
if ([self.mainSession roomWithRoomId:currentRoomId].isDirect)
|
||||
{
|
||||
title = [VectorL10n roomParticipantsLeavePromptTitleForDm];
|
||||
message = [VectorL10n roomParticipantsLeavePromptMsgForDm];
|
||||
}
|
||||
else
|
||||
{
|
||||
title = [VectorL10n roomParticipantsLeavePromptTitle];
|
||||
message = [VectorL10n roomParticipantsLeavePromptMsg];
|
||||
}
|
||||
|
||||
// confirm leave
|
||||
UIAlertController *leavePrompt = [UIAlertController alertControllerWithTitle:title
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
MXWeakify(self);
|
||||
[leavePrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
}]];
|
||||
|
||||
[leavePrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n leave]
|
||||
style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
|
||||
// Check whether the user didn't leave the room yet
|
||||
// TODO: Handle multi-account
|
||||
if (weakRoom)
|
||||
{
|
||||
[self startActivityIndicatorWithLabel:[VectorL10n roomParticipantsLeaveProcessing]];
|
||||
// cancel pending uploads/downloads
|
||||
// they are useless by now
|
||||
[MXMediaManager cancelDownloadsInCacheFolder:weakRoom.roomId];
|
||||
|
||||
// TODO GFO cancel pending uploads related to this room
|
||||
|
||||
MXLogDebug(@"[RecentsViewController] Leave room (%@)", weakRoom.roomId);
|
||||
|
||||
MXWeakify(self);
|
||||
[weakRoom leave:^{
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
[self stopActivityIndicator];
|
||||
[self.userIndicatorStore presentSuccessWithLabel:[VectorL10n roomParticipantsLeaveSuccess]];
|
||||
// Force table refresh
|
||||
[self cancelEditionMode:YES];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
MXLogDebug(@"[RecentsViewController] Failed to leave room");
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
// Notify the end user
|
||||
NSString *userId = room.mxSession.myUser.userId;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification
|
||||
object:error
|
||||
userInfo:userId ? @{kMXKErrorUserIdKey: userId} : nil];
|
||||
|
||||
[self stopActivityIndicator];
|
||||
|
||||
// Leave editing mode
|
||||
[self cancelEditionMode:self->isRefreshPending];
|
||||
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leave editing mode
|
||||
[self cancelEditionMode:self->isRefreshPending];
|
||||
}
|
||||
|
||||
}]];
|
||||
[leavePrompt mxk_setAccessibilityIdentifier:@"LeaveEditedRoomAlert"];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self presentViewController:leavePrompt animated:YES completion:nil];
|
||||
self->currentAlert = leavePrompt;
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,19 +132,31 @@ class RoomContextActionService: NSObject, RoomContextActionServiceProtocol {
|
||||
|
||||
func leaveRoom(promptUser: Bool) {
|
||||
guard promptUser else {
|
||||
// Only used for declining an invite
|
||||
self.leaveRoom()
|
||||
return
|
||||
}
|
||||
|
||||
let title = room.isDirect ? VectorL10n.roomParticipantsLeavePromptTitleForDm : VectorL10n.roomParticipantsLeavePromptTitle
|
||||
let message = room.isDirect ? VectorL10n.roomParticipantsLeavePromptMsgForDm : VectorL10n.roomParticipantsLeavePromptMsg
|
||||
|
||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
alertController.addAction(UIAlertAction(title: VectorL10n.cancel, style: .cancel, handler: nil))
|
||||
alertController.addAction(UIAlertAction(title: VectorL10n.leave, style: .default, handler: { action in
|
||||
self.leaveRoom()
|
||||
}))
|
||||
self.delegate?.roomContextActionService(self, presentAlert: alertController)
|
||||
Task {
|
||||
if try await room.isLastOwner() {
|
||||
await MainActor.run {
|
||||
let alertController = UIAlertController(title: VectorL10n.error, message: VectorL10n.roomParticipantsLeaveNotAllowedForLastOwnerMsg, preferredStyle: .alert)
|
||||
alertController.addAction(UIAlertAction(title: VectorL10n.ok, style: .cancel, handler: nil))
|
||||
self.delegate?.roomContextActionService(self, presentAlert: alertController)
|
||||
}
|
||||
} else {
|
||||
let title = room.isDirect ? VectorL10n.roomParticipantsLeavePromptTitleForDm : VectorL10n.roomParticipantsLeavePromptTitle
|
||||
let message = room.isDirect ? VectorL10n.roomParticipantsLeavePromptMsgForDm : VectorL10n.roomParticipantsLeavePromptMsg
|
||||
await MainActor.run {
|
||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
alertController.addAction(UIAlertAction(title: VectorL10n.cancel, style: .cancel, handler: nil))
|
||||
alertController.addAction(UIAlertAction(title: VectorL10n.leave, style: .default, handler: { [weak self] action in
|
||||
self?.leaveRoom()
|
||||
}))
|
||||
self.delegate?.roomContextActionService(self, presentAlert: alertController)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func joinRoom() {
|
||||
|
||||
@@ -220,20 +220,47 @@ Please see LICENSE in the repository root for full details.
|
||||
}
|
||||
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];
|
||||
|
||||
MXWeakify(self);
|
||||
[self.mxRoom isLastOwnerWithCompletionHandler:^(BOOL isLastOwner, NSError* error){
|
||||
if (isLastOwner)
|
||||
{
|
||||
UIAlertController *isLastOwnerPrompt = [UIAlertController alertControllerWithTitle:[VectorL10n error]
|
||||
message:[VectorL10n roomParticipantsLeaveNotAllowedForLastOwnerMsg]
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[isLastOwnerPrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n ok]
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction * action) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
}]];
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self presentViewController:isLastOwnerPrompt animated:YES completion:nil];
|
||||
self->currentAlert = isLastOwnerPrompt;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
[self addPendingActionMask];
|
||||
MXWeakify(self);
|
||||
[self.mxRoom leave:^{
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
[self removePendingActionMask];
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
[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;
|
||||
}
|
||||
|
||||
@@ -1231,62 +1231,7 @@ Please see LICENSE in the repository root for full details.
|
||||
|
||||
if (section == participantsSection && userParticipant && (0 == row) && !currentSearchText.length)
|
||||
{
|
||||
// Leave ?
|
||||
MXWeakify(self);
|
||||
|
||||
NSString *title, *message;
|
||||
if (self.mxRoom.isDirect)
|
||||
{
|
||||
title = [VectorL10n roomParticipantsLeavePromptTitleForDm];
|
||||
message = [VectorL10n roomParticipantsLeavePromptMsgForDm];
|
||||
}
|
||||
else
|
||||
{
|
||||
title = [VectorL10n roomParticipantsLeavePromptTitle];
|
||||
message = [VectorL10n roomParticipantsLeavePromptMsg];
|
||||
}
|
||||
|
||||
currentAlert = [UIAlertController alertControllerWithTitle:title
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
|
||||
}]];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n leave]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
|
||||
[self addPendingActionMask];
|
||||
MXWeakify(self);
|
||||
[self.mxRoom leave:^{
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
[self removePendingActionMask];
|
||||
MXLogDebug(@"[RoomParticipantsVC] Leave room %@ failed", self.mxRoom.roomId);
|
||||
// Alert user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
}];
|
||||
|
||||
}]];
|
||||
|
||||
[currentAlert mxk_setAccessibilityIdentifier:@"RoomParticipantsVCLeaveAlert"];
|
||||
[self presentViewController:currentAlert animated:YES completion:nil];
|
||||
[self leaveRoom];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1433,6 +1378,90 @@ Please see LICENSE in the repository root for full details.
|
||||
}
|
||||
}
|
||||
|
||||
- (void)leaveRoom {
|
||||
MXWeakify(self);
|
||||
|
||||
[self.mxRoom isLastOwnerWithCompletionHandler:^(BOOL isLastOwner, NSError* error) {
|
||||
if (isLastOwner)
|
||||
{
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = [UIAlertController alertControllerWithTitle:[VectorL10n error]
|
||||
message:[VectorL10n roomParticipantsLeaveNotAllowedForLastOwnerMsg]
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
}]];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self presentViewController:self->currentAlert animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leave ?
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
NSString *title, *message;
|
||||
if (self.mxRoom.isDirect)
|
||||
{
|
||||
title = [VectorL10n roomParticipantsLeavePromptTitleForDm];
|
||||
message = [VectorL10n roomParticipantsLeavePromptMsgForDm];
|
||||
}
|
||||
else
|
||||
{
|
||||
title = [VectorL10n roomParticipantsLeavePromptTitle];
|
||||
message = [VectorL10n roomParticipantsLeavePromptMsg];
|
||||
}
|
||||
|
||||
self->currentAlert = [UIAlertController alertControllerWithTitle:title
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
MXWeakify(self);
|
||||
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
|
||||
}]];
|
||||
|
||||
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n leave]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
self->currentAlert = nil;
|
||||
|
||||
[self addPendingActionMask];
|
||||
MXWeakify(self);
|
||||
[self.mxRoom leave:^{
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
[self removePendingActionMask];
|
||||
MXLogDebug(@"[RoomParticipantsVC] Leave room %@ failed", self.mxRoom.roomId);
|
||||
// Alert user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
}];
|
||||
|
||||
}]];
|
||||
|
||||
[self->currentAlert mxk_setAccessibilityIdentifier:@"RoomParticipantsVCLeaveAlert"];
|
||||
[self presentViewController:self->currentAlert animated:YES completion:nil];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)onCancel:(id)sender
|
||||
{
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
|
||||
@@ -75,6 +75,17 @@ final class RoomInfoListViewController: UIViewController {
|
||||
return controller
|
||||
}()
|
||||
|
||||
private lazy var isLastOwnerAlertController: UIAlertController = {
|
||||
let title = VectorL10n.error
|
||||
let message = VectorL10n.roomParticipantsLeaveNotAllowedForLastOwnerMsg
|
||||
let controller = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
||||
controller.addAction(UIAlertAction(title: VectorL10n.ok, style: .default, handler: nil))
|
||||
controller.mxk_setAccessibilityIdentifier("RoomSettingsVCLastOwnerAlert")
|
||||
|
||||
return controller
|
||||
}()
|
||||
|
||||
private enum RowType {
|
||||
case `default`
|
||||
case destructive
|
||||
@@ -216,7 +227,11 @@ final class RoomInfoListViewController: UIViewController {
|
||||
VectorL10n.roomParticipantsLeavePromptTitleForDm :
|
||||
VectorL10n.roomParticipantsLeavePromptTitle
|
||||
let rowLeave = Row(type: .destructive, icon: Asset.Images.roomActionLeave.image, text: leaveTitle, accessoryType: .none) {
|
||||
self.present(self.leaveAlertController, animated: true, completion: nil)
|
||||
if viewData.isLastOwner {
|
||||
self.present(self.isLastOwnerAlertController, animated: true, completion: nil)
|
||||
} else {
|
||||
self.present(self.leaveAlertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
let sectionLeave = Section(header: nil,
|
||||
rows: [rowLeave],
|
||||
|
||||
@@ -24,4 +24,5 @@ struct RoomInfoListViewData {
|
||||
let isEncrypted: Bool
|
||||
let isDirect: Bool
|
||||
let basicInfoViewData: RoomInfoBasicViewData
|
||||
let isLastOwner: Bool
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ final class RoomInfoListViewModel: NSObject, RoomInfoListViewModelType {
|
||||
|
||||
private let session: MXSession
|
||||
private let room: MXRoom
|
||||
private var isLastOwner = false
|
||||
|
||||
// MARK: Public
|
||||
|
||||
@@ -51,7 +52,8 @@ final class RoomInfoListViewModel: NSObject, RoomInfoListViewModelType {
|
||||
return RoomInfoListViewData(numberOfMembers: Int(room.summary.membersCount.joined),
|
||||
isEncrypted: room.summary.isEncrypted,
|
||||
isDirect: room.isDirect,
|
||||
basicInfoViewData: basicInfoViewData)
|
||||
basicInfoViewData: basicInfoViewData,
|
||||
isLastOwner: isLastOwner)
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
@@ -97,6 +99,9 @@ final class RoomInfoListViewModel: NSObject, RoomInfoListViewModelType {
|
||||
@objc private func roomSummaryUpdated(_ notification: Notification) {
|
||||
// force update view
|
||||
self.update(viewState: .loaded(viewData: viewData))
|
||||
Task {
|
||||
isLastOwner = (try? await room.isLastOwner()) == true
|
||||
}
|
||||
}
|
||||
|
||||
private func loadData() {
|
||||
|
||||
@@ -2624,23 +2624,6 @@ static CGSize kThreadListBarButtonItemImageSize;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)leaveRoom
|
||||
{
|
||||
[self startActivityIndicator];
|
||||
|
||||
[self.roomDataSource.room leave:^{
|
||||
|
||||
[self stopActivityIndicator];
|
||||
[self notifyDelegateOnLeaveRoomIfNecessary];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
[self stopActivityIndicator];
|
||||
MXLogDebug(@"[RoomVC] Failed to reject an invited room (%@) failed", self.roomDataSource.room.roomId);
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)notifyDelegateOnLeaveRoomIfNecessary {
|
||||
if (isRoomLeft) {
|
||||
return;
|
||||
|
||||
1
changelog.d/7951.change
Normal file
1
changelog.d/7951.change
Normal file
@@ -0,0 +1 @@
|
||||
PL 150 users will be displayed as Owners.
|
||||
1
changelog.d/7952.change
Normal file
1
changelog.d/7952.change
Normal file
@@ -0,0 +1 @@
|
||||
Owners can't leave the room if they are the last owners while also not being the last member.
|
||||
Submodule matrix-ios-sdk updated: d1fe456f97...b0f967c9c8
Reference in New Issue
Block a user