diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 6b009f949..28a550f97 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3966,5 +3966,6 @@ "wordsPerMinute": "Words per minute", "leaveRoomDescription": "The chat will be moved to the archive. Other users will be able to see that you have left the chat.", "archiveSpaceDescription": "All chats within this space will be moved to the archive for yourself and other non-admin users.", - "leaveSpaceDescription": "All chats within this space will be moved to the archive. Other users will be able to see that you have left the space." + "leaveSpaceDescription": "All chats within this space will be moved to the archive. Other users will be able to see that you have left the space.", + "onlyAdminDescription": "Since there are no other admins, all other participants will also be removed." } \ No newline at end of file diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index c976e5bcb..d45f50f15 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -597,6 +597,8 @@ class ChatDetailsView extends StatelessWidget { onTap: () async { OkCancelResult confirmed = OkCancelResult.ok; bool shouldGo = false; + // If user is only admin, room will be archived + final bool onlyAdmin = await room.isOnlyAdmin(); // archiveSpace has its own popup; only show if not space if (!room.isSpace) { confirmed = await showOkCancelAlertDialog( @@ -605,20 +607,30 @@ class ChatDetailsView extends StatelessWidget { title: L10n.of(context)!.areYouSure, okLabel: L10n.of(context)!.ok, cancelLabel: L10n.of(context)!.cancel, - message: L10n.of(context)!.leaveRoomDescription, + message: onlyAdmin + ? L10n.of(context)!.onlyAdminDescription + : L10n.of(context)!.leaveRoomDescription, ); } if (confirmed == OkCancelResult.ok) { if (room.isSpace) { - shouldGo = await room.leaveSpace( - context, - Matrix.of(context).client, - ); + shouldGo = onlyAdmin + ? await room.archiveSpace( + context, + Matrix.of(context).client, + onlyAdmin: true, + ) + : await room.leaveSpace( + context, + Matrix.of(context).client, + ); } else { final success = await showFutureLoadingDialog( context: context, future: () async { - await room.leave(); + onlyAdmin + ? await room.archive() + : await room.leave(); }, ); shouldGo = (success.error == null); diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 9f2ca0b48..6facc381d 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -682,13 +682,20 @@ class ChatListController extends State // #Pangea Future leaveAction() async { + final bool onlyAdmin = await Matrix.of(context) + .client + .getRoomById(selectedRoomIds.first) + ?.isOnlyAdmin() ?? + false; final confirmed = await showOkCancelAlertDialog( useRootNavigator: false, context: context, title: L10n.of(context)!.areYouSure, okLabel: L10n.of(context)!.yes, cancelLabel: L10n.of(context)!.cancel, - message: L10n.of(context)!.leaveRoomDescription, + message: onlyAdmin + ? L10n.of(context)!.onlyAdminDescription + : L10n.of(context)!.leaveRoomDescription, ) == OkCancelResult.ok; if (!confirmed) return; @@ -696,7 +703,7 @@ class ChatListController extends State selectedRoomIds.contains(Matrix.of(context).activeRoomId); await showFutureLoadingDialog( context: context, - future: () => _leaveSelectedRooms(), + future: () => _leaveSelectedRooms(onlyAdmin), ); setState(() {}); if (leftActiveRoom) { @@ -765,7 +772,7 @@ class ChatListController extends State } // #Pangea - Future _leaveSelectedRooms() async { + Future _leaveSelectedRooms(bool onlyAdmin) async { final client = Matrix.of(context).client; while (selectedRoomIds.isNotEmpty) { final roomId = selectedRoomIds.first; @@ -776,7 +783,7 @@ class ChatListController extends State room.isUnread) { await room.markUnread(false); } - await room.leave(); + onlyAdmin ? await room.archive() : await room.leave(); } finally { toggleSelection(roomId); } diff --git a/lib/pages/chat_list/chat_list_header.dart b/lib/pages/chat_list/chat_list_header.dart index 9a3aab506..41b9a6bf1 100644 --- a/lib/pages/chat_list/chat_list_header.dart +++ b/lib/pages/chat_list/chat_list_header.dart @@ -171,6 +171,17 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget { onPressed: controller.toggleMuted, ), // #Pangea + if (controller.selectedRoomIds.length == 1 && + !(Matrix.of(context) + .client + .getRoomById(controller.selectedRoomIds.single) + ?.isRoomAdmin ?? + false)) + IconButton( + icon: const Icon(Icons.arrow_forward), + tooltip: L10n.of(context)!.leave, + onPressed: controller.leaveAction, + ), if (controller.selectedRoomIds.length == 1 && (Matrix.of(context) .client diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index 351af511d..c8d9b5ef7 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -229,7 +229,7 @@ class _SpaceViewState extends State { ), message: spaceChild?.topic ?? room?.topic, actions: [ - if (room == null) + if (room == null || room.membership == Membership.leave) SheetAction( key: SpaceChildContextAction.join, label: L10n.of(context)!.joinRoom, @@ -254,7 +254,9 @@ class _SpaceViewState extends State { label: L10n.of(context)!.addToSpace, icon: Icons.workspaces_outlined, ), - if (room != null && room.isRoomAdmin) + if (room != null && + room.isRoomAdmin && + room.membership != Membership.leave) SheetAction( key: SpaceChildContextAction.archive, label: room.isSpace @@ -263,8 +265,10 @@ class _SpaceViewState extends State { icon: Icons.architecture_outlined, isDestructiveAction: true, ), - // Pangea# - if (room != null) + + if (room != null && room.membership != Membership.leave) + // if (room != null) + // Pangea# SheetAction( key: SpaceChildContextAction.leave, label: L10n.of(context)!.leave, @@ -287,10 +291,16 @@ class _SpaceViewState extends State { widget.controller.cancelAction(); if (room == null) return; if (room.isSpace) { - await room.leaveSpace( - context, - Matrix.of(context).client, - ); + await room.isOnlyAdmin() + ? await room.archiveSpace( + context, + Matrix.of(context).client, + onlyAdmin: true, + ) + : await room.leaveSpace( + context, + Matrix.of(context).client, + ); } else { widget.controller.toggleSelection(room.id); await widget.controller.leaveAction(); diff --git a/lib/pangea/extensions/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension.dart index bd996bc5e..62ba97c86 100644 --- a/lib/pangea/extensions/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension.dart @@ -835,14 +835,43 @@ extension PangeaRoom on Room { } } - Future archiveSpace(BuildContext context, Client client) async { + // If there are no other admins, and at least one non-admin, return true + Future isOnlyAdmin() async { + if (!isRoomAdmin) { + return false; + } + final List participants = await requestParticipants(); + + return ((participants + .where( + (e) => + e.powerLevel == ClassDefaultValues.powerLevelOfAdmin && + e.id != BotName.byEnvironment, + ) + .toList() + .length) == + 1) && + (participants + .where( + (e) => + e.powerLevel < ClassDefaultValues.powerLevelOfAdmin && + e.id != BotName.byEnvironment, + ) + .toList()) + .isNotEmpty; + } + + Future archiveSpace(BuildContext context, Client client, + {bool onlyAdmin = false}) async { final confirmed = await showOkCancelAlertDialog( useRootNavigator: false, context: context, title: L10n.of(context)!.areYouSure, okLabel: L10n.of(context)!.yes, cancelLabel: L10n.of(context)!.cancel, - message: L10n.of(context)!.archiveSpaceDescription, + message: onlyAdmin + ? L10n.of(context)!.onlyAdminDescription + : L10n.of(context)!.archiveSpaceDescription, ) == OkCancelResult.ok; if (!confirmed) return false; diff --git a/lib/widgets/chat_settings_popup_menu.dart b/lib/widgets/chat_settings_popup_menu.dart index fe4a45c44..55672911e 100644 --- a/lib/widgets/chat_settings_popup_menu.dart +++ b/lib/widgets/chat_settings_popup_menu.dart @@ -200,18 +200,22 @@ class ChatSettingsPopupMenuState extends State { break; // Pangea# case 'leave': + final bool onlyAdmin = await widget.room.isOnlyAdmin(); final confirmed = await showOkCancelAlertDialog( useRootNavigator: false, context: context, title: L10n.of(context)!.areYouSure, okLabel: L10n.of(context)!.ok, cancelLabel: L10n.of(context)!.cancel, - message: L10n.of(context)!.leaveRoomDescription, + message: onlyAdmin + ? L10n.of(context)!.onlyAdminDescription + : L10n.of(context)!.leaveRoomDescription, ); if (confirmed == OkCancelResult.ok) { final success = await showFutureLoadingDialog( context: context, - future: () => widget.room.leave(), + future: () => + onlyAdmin ? widget.room.archive() : widget.room.leave(), ); if (success.error == null) { context.go('/rooms');