diff --git a/lib/features/mailbox/domain/state/delete_multiple_mailbox_state.dart b/lib/features/mailbox/domain/state/delete_multiple_mailbox_state.dart index 8eb972c45..be34b8462 100644 --- a/lib/features/mailbox/domain/state/delete_multiple_mailbox_state.dart +++ b/lib/features/mailbox/domain/state/delete_multiple_mailbox_state.dart @@ -1,19 +1,40 @@ -import 'package:core/core.dart'; +import 'package:core/presentation/state/failure.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:tmail_ui_user/features/base/state/ui_action_state.dart'; -class DeleteMultipleMailboxSuccess extends UIActionState { +class DeleteMultipleMailboxAllSuccess extends UIActionState { - final MailboxId mailboxIdDeleted; + final List listMailboxIdDeleted; - DeleteMultipleMailboxSuccess(this.mailboxIdDeleted, { + DeleteMultipleMailboxAllSuccess(this.listMailboxIdDeleted, { jmap.State? currentEmailState, jmap.State? currentMailboxState, }) : super(currentEmailState, currentMailboxState); @override - List get props => [mailboxIdDeleted]; + List get props => [listMailboxIdDeleted]; +} + +class DeleteMultipleMailboxHasSomeSuccess extends UIActionState { + + final List listMailboxIdDeleted; + + DeleteMultipleMailboxHasSomeSuccess(this.listMailboxIdDeleted, { + jmap.State? currentEmailState, + jmap.State? currentMailboxState, + }) : super(currentEmailState, currentMailboxState); + + @override + List get props => [listMailboxIdDeleted]; +} + +class DeleteMultipleMailboxAllFailure extends FeatureFailure { + + DeleteMultipleMailboxAllFailure(); + + @override + List get props => []; } class DeleteMultipleMailboxFailure extends FeatureFailure { diff --git a/lib/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart b/lib/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart index f296f37ce..fa825c4f5 100644 --- a/lib/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart +++ b/lib/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart @@ -1,4 +1,6 @@ -import 'package:core/core.dart'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/app_logger.dart'; import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; @@ -11,17 +13,38 @@ class DeleteMultipleMailboxInteractor { DeleteMultipleMailboxInteractor(this._mailboxRepository); - Stream> execute(Session session, AccountId accountId, List mailboxIds, MailboxId mailboxIdDeleted) async* { + Stream> execute( + Session session, + AccountId accountId, + Map> mapMailboxIdToDelete, + List listMailboxIdToDelete + ) async* { try { final currentMailboxState = await _mailboxRepository.getMailboxState(); - final result = await _mailboxRepository.deleteMultipleMailbox(session, accountId, mailboxIds); - if (result) { - yield Right(DeleteMultipleMailboxSuccess( - mailboxIdDeleted, + + final listResult = await Future.wait( + mapMailboxIdToDelete.keys.map((mailboxId) { + final mailboxIdsToDelete = mapMailboxIdToDelete[mailboxId]!; + return _mailboxRepository.deleteMultipleMailbox( + session, + accountId, + mailboxIdsToDelete); + }) + ); + + final allSuccess = listResult.every((result) => result); + final allFailed = listResult.every((result) => !result); + + if (allSuccess) { + yield Right(DeleteMultipleMailboxAllSuccess( + listMailboxIdToDelete, currentMailboxState: currentMailboxState)); + } else if (allFailed) { + yield Left(DeleteMultipleMailboxAllFailure()); } else { - logError('DeleteMultipleMailboxInteractor::execute(): failed'); - yield Left(DeleteMultipleMailboxFailure(null)); + yield Right(DeleteMultipleMailboxHasSomeSuccess( + listMailboxIdToDelete, + currentMailboxState: currentMailboxState)); } } catch (e) { logError('DeleteMultipleMailboxInteractor::execute(): exception: $e'); diff --git a/lib/features/mailbox/presentation/extensions/list_mailbox_node_extension.dart b/lib/features/mailbox/presentation/extensions/list_mailbox_node_extension.dart new file mode 100644 index 000000000..8338a14ed --- /dev/null +++ b/lib/features/mailbox/presentation/extensions/list_mailbox_node_extension.dart @@ -0,0 +1,7 @@ + +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; + +extension ListMailboxNodeExtension on List { + List get mailboxIds => map((node) => node.item.id).toList(); +} \ No newline at end of file diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index 81a9327a8..bc23a4cb0 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -42,6 +42,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/usecases/move_mailbox_inte import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/rename_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/search_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/extensions/list_mailbox_node_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories_expand_mode.dart'; @@ -173,8 +174,10 @@ class MailboxController extends BaseMailboxController { _createNewMailboxSuccess(success); } else if (success is SearchMailboxSuccess) { _searchMailboxSuccess(success); - } else if (success is DeleteMultipleMailboxSuccess) { - _deleteMailboxSuccess(success); + } else if (success is DeleteMultipleMailboxAllSuccess) { + _deleteMultipleMailboxSuccess(success.listMailboxIdDeleted, success.currentMailboxState); + } else if (success is DeleteMultipleMailboxHasSomeSuccess) { + _deleteMultipleMailboxSuccess(success.listMailboxIdDeleted, success.currentMailboxState); } else if ((success is GetAllMailboxSuccess || success is RefreshChangesAllMailboxSuccess) && isSearchActive()) { _searchMailboxAction(allMailboxes, searchQuery.value); } else if (success is RenameMailboxSuccess) { @@ -533,29 +536,35 @@ class MailboxController extends BaseMailboxController { } } - void pressMailboxSelectionAction(BuildContext context, MailboxActions actions, - List selectionMailbox) { + void pressMailboxSelectionAction( + BuildContext context, + MailboxActions actions, + List selectedMailboxList + ) { switch(actions) { case MailboxActions.delete: - _openConfirmationDialogDeleteMailboxAction(context, selectionMailbox.first); + _openConfirmationDialogDeleteMultipleMailboxAction(context, selectedMailboxList); break; case MailboxActions.rename: - _openDialogRenameMailboxAction(context, selectionMailbox.first); + _openDialogRenameMailboxAction(context, selectedMailboxList.first); break; case MailboxActions.markAsRead: - _markAsReadMailboxAction(context, selectionMailbox.first); + _markAsReadMailboxAction(context, selectedMailboxList.first); break; case MailboxActions.move: - _moveMailboxAction(selectionMailbox.first); + _moveMailboxAction(selectedMailboxList.first); break; default: break; } } - void _openConfirmationDialogDeleteMailboxAction(BuildContext context, - PresentationMailbox presentationMailbox) { - if (_responsiveUtils.isMobile(context)) { + void _openConfirmationDialogDeleteMailboxAction( + BuildContext context, + PresentationMailbox presentationMailbox + ) { + if (_responsiveUtils.isLandscapeMobile(context) || + _responsiveUtils.isPortraitMobile(context)) { (ConfirmationDialogActionSheetBuilder(context) ..messageText(AppLocalizations.of(context) .message_confirmation_dialog_delete_mailbox(presentationMailbox.name?.name ?? '')) @@ -590,42 +599,132 @@ class MailboxController extends BaseMailboxController { } void _deleteMailboxAction(PresentationMailbox presentationMailbox) { - final matchedNode = findMailboxNodeById(presentationMailbox.id); - final accountId = mailboxDashBoardController.accountId.value; final session = mailboxDashBoardController.sessionCurrent; - if (session != null) { - if (matchedNode != null && accountId != null) { - final descendantIds = matchedNode.descendantsAsList() - .map((node) => node.item.id) - .toList(); + if (session != null && accountId != null) { + final tupleMap = _generateMapDescendantIdsAndMailboxIdList([presentationMailbox]); + final mapDescendantIds = tupleMap.value1; + final listMailboxId = tupleMap.value2; - final descendantIdsReversed = descendantIds.reversed.toList(); - consumeState(_deleteMultipleMailboxInteractor.execute( - session, accountId, descendantIdsReversed, presentationMailbox.id)); - } else { - _deleteMailboxFailure(DeleteMultipleMailboxFailure(null)); - } + consumeState(_deleteMultipleMailboxInteractor.execute( + session, + accountId, + mapDescendantIds, + listMailboxId)); + } else { + _deleteMailboxFailure(DeleteMultipleMailboxFailure(null)); } _cancelSelectMailbox(); popBack(); } - void _deleteMailboxSuccess(DeleteMultipleMailboxSuccess success) { + void _deleteMultipleMailboxSuccess( + List listMailboxIdDeleted, + jmap.State? currentMailboxState + ) { if (currentOverlayContext != null && currentContext != null) { _appToast.showToastWithIcon( currentOverlayContext!, message: AppLocalizations.of(currentContext!).delete_mailboxes_successfully, icon: _imagePaths.icSelected); } - if (success.mailboxIdDeleted == mailboxDashBoardController.selectedMailbox.value?.id) { + if (listMailboxIdDeleted.contains(mailboxDashBoardController.selectedMailbox.value?.id)) { _switchBackToMailboxDefault(); } - refreshMailboxChanges(currentMailboxState: success.currentMailboxState); + refreshMailboxChanges(currentMailboxState: currentMailboxState); } + void _openConfirmationDialogDeleteMultipleMailboxAction( + BuildContext context, + List selectedMailboxList + ) { + if (_responsiveUtils.isLandscapeMobile(context) || + _responsiveUtils.isPortraitMobile(context)) { + (ConfirmationDialogActionSheetBuilder(context) + ..messageText(AppLocalizations.of(context) + .messageConfirmationDialogDeleteMultipleMailbox(selectedMailboxList.length)) + ..onCancelAction(AppLocalizations.of(context).cancel, () => + popBack()) + ..onConfirmAction(AppLocalizations.of(context).delete, () => + _deleteMultipleMailboxAction(selectedMailboxList))) + .show(); + } else { + showDialog( + context: context, + barrierColor: AppColor.colorDefaultCupertinoActionSheet, + builder: (BuildContext context) => PointerInterceptor(child: (ConfirmDialogBuilder(_imagePaths) + ..key(const Key('confirm_dialog_delete_multiple_mailbox')) + ..title(AppLocalizations.of(context).delete_mailboxes) + ..content(AppLocalizations.of(context) + .messageConfirmationDialogDeleteMultipleMailbox(selectedMailboxList.length)) + ..addIcon(SvgPicture.asset(_imagePaths.icRemoveDialog, + fit: BoxFit.fill)) + ..colorConfirmButton(AppColor.colorConfirmActionDialog) + ..styleTextConfirmButton(const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w500, + color: AppColor.colorActionDeleteConfirmDialog)) + ..onCloseButtonAction(() => popBack()) + ..onConfirmButtonAction(AppLocalizations.of(context).delete, () => + _deleteMultipleMailboxAction(selectedMailboxList)) + ..onCancelButtonAction(AppLocalizations.of(context).cancel, () => + popBack())) + .build())); + } + } + + Tuple2>, List> _generateMapDescendantIdsAndMailboxIdList( + List selectedMailboxList + ) { + Map> mapDescendantIds = {}; + List allMailboxIds = []; + + for (var mailbox in selectedMailboxList) { + final currentMailboxId = mailbox.id; + + if (allMailboxIds.contains(currentMailboxId)) { + continue; + } else { + final matchedNode = findMailboxNodeById(currentMailboxId); + + if (matchedNode != null) { + final descendantIds = matchedNode.descendantsAsList().mailboxIds; + final descendantIdsReversed = descendantIds.reversed.toList(); + + mapDescendantIds[currentMailboxId] = descendantIdsReversed; + allMailboxIds.addAll(descendantIdsReversed); + } + } + } + + log('MailboxController::_generateMapDescendantIdsByMailboxList(): mapDescendantIds: $mapDescendantIds'); + + return Tuple2(mapDescendantIds, allMailboxIds); + } + + void _deleteMultipleMailboxAction(List selectedMailboxList) { + final accountId = mailboxDashBoardController.accountId.value; + final session = mailboxDashBoardController.sessionCurrent; + + if (session != null && accountId != null) { + final tupleMap = _generateMapDescendantIdsAndMailboxIdList(selectedMailboxList); + final mapDescendantIds = tupleMap.value1; + final listMailboxId = tupleMap.value2; + consumeState(_deleteMultipleMailboxInteractor.execute( + session, + accountId, + mapDescendantIds, + listMailboxId)); + } else { + _deleteMailboxFailure(DeleteMultipleMailboxFailure(null)); + } + + _cancelSelectMailbox(); + popBack(); + } + void _switchBackToMailboxDefault() { final inboxMailbox = findMailboxNodeByRole(PresentationMailbox.roleInbox); mailboxDashBoardController.setSelectedMailbox(inboxMailbox?.item); @@ -817,19 +916,22 @@ class MailboxController extends BaseMailboxController { final allChildrenAtMailboxLocation = (defaultMailboxTree.value.root.childrenItems ?? []) + (folderMailboxTree.value.root.childrenItems ?? []); if (allChildrenAtMailboxLocation.isNotEmpty) { listMailboxNameAsStringExist = allChildrenAtMailboxLocation - .where((mailboxNode) => mailboxNode.item.name != null && mailboxNode.item.name?.name.isNotEmpty == true) - .map((mailboxNode) => mailboxNode.item.name!.name) + .where((mailboxNode) => mailboxNode.nameNotEmpty) + .map((mailboxNode) => mailboxNode.mailboxNameAsString) .toList(); + } else { + listMailboxNameAsStringExist = []; } } else { - final mailboxNodeLocation = defaultMailboxTree.value.findNode((node) => node.item.id == mailboxRenamed.parentId) - ?? folderMailboxTree.value.findNode((node) => node.item.id == mailboxRenamed.parentId); + final mailboxNodeLocation = findMailboxNodeById(mailboxRenamed.parentId!); if (mailboxNodeLocation != null && mailboxNodeLocation.childrenItems?.isNotEmpty == true) { final allChildrenAtMailboxLocation = mailboxNodeLocation.childrenItems!; listMailboxNameAsStringExist = allChildrenAtMailboxLocation - .where((mailboxNode) => mailboxNode.item.name != null && mailboxNode.item.name?.name.isNotEmpty == true) - .map((mailboxNode) => mailboxNode.item.name!.name) + .where((mailboxNode) => mailboxNode.nameNotEmpty) + .map((mailboxNode) => mailboxNode.mailboxNameAsString) .toList(); + } else { + listMailboxNameAsStringExist = []; } } @@ -863,8 +965,11 @@ class MailboxController extends BaseMailboxController { mailboxDashBoardController.showAppDashboardAction(); } - void handleMailboxAction(BuildContext context, MailboxActions actions, - PresentationMailbox mailbox) { + void handleMailboxAction( + BuildContext context, + MailboxActions actions, + PresentationMailbox mailbox + ) { popBack(); switch(actions) { diff --git a/lib/features/mailbox/presentation/model/mailbox_node.dart b/lib/features/mailbox/presentation/model/mailbox_node.dart index d3e3ed00d..bbfe686fd 100644 --- a/lib/features/mailbox/presentation/model/mailbox_node.dart +++ b/lib/features/mailbox/presentation/model/mailbox_node.dart @@ -33,6 +33,10 @@ class MailboxNode with EquatableMixin{ } ); + bool get nameNotEmpty => item.name?.name.isNotEmpty == true; + + String get mailboxNameAsString => item.name?.name ?? ''; + void addChildNode(MailboxNode node) { childrenItems ??= []; childrenItems?.add(node); diff --git a/lib/features/mailbox/presentation/widgets/bottom_bar_selection_mailbox_widget.dart b/lib/features/mailbox/presentation/widgets/bottom_bar_selection_mailbox_widget.dart index 5ff85cb0f..3adcbce54 100644 --- a/lib/features/mailbox/presentation/widgets/bottom_bar_selection_mailbox_widget.dart +++ b/lib/features/mailbox/presentation/widgets/bottom_bar_selection_mailbox_widget.dart @@ -117,8 +117,7 @@ class BottomBarSelectionMailboxWidget { ); } - bool get _isDeleteMailboxValid => _listSelectionMailbox.length == 1 - && _isAllFolderMailbox; + bool get _isDeleteMailboxValid => _isAllFolderMailbox; bool get _isRenameMailboxValid => _listSelectionMailbox.length == 1 && _isAllFolderMailbox; diff --git a/lib/features/mailbox_creator/presentation/mailbox_creator_controller.dart b/lib/features/mailbox_creator/presentation/mailbox_creator_controller.dart index c958effcb..3de21d314 100644 --- a/lib/features/mailbox_creator/presentation/mailbox_creator_controller.dart +++ b/lib/features/mailbox_creator/presentation/mailbox_creator_controller.dart @@ -48,8 +48,6 @@ class MailboxCreatorController extends BaseController { folderMailboxTree = arguments.folderMailboxTree; defaultMailboxTree = arguments.defaultMailboxTree; accountId = arguments.accountId; - log('MailboxCreatorController::onReady(): defaultMailboxTree: $defaultMailboxTree'); - log('MailboxCreatorController::onReady(): folderMailboxTree: $folderMailboxTree'); _createListMailboxNameAsStringInMailboxLocation(); } } @@ -80,24 +78,35 @@ class MailboxCreatorController extends BaseController { return true; } + MailboxNode? _findMailboxNodeById(MailboxId mailboxId) { + final mailboxNode = defaultMailboxTree?.findNode((node) => node.item.id == mailboxId); + if (mailboxNode != null) { + return mailboxNode; + } + return folderMailboxTree?.findNode((node) => node.item.id == mailboxId); + } + void _createListMailboxNameAsStringInMailboxLocation() { if (selectedMailbox.value == null) { final allChildrenAtMailboxLocation = (defaultMailboxTree?.root.childrenItems ?? []) + (folderMailboxTree?.root.childrenItems ?? []); if (allChildrenAtMailboxLocation.isNotEmpty) { listMailboxNameAsStringExist = allChildrenAtMailboxLocation - .where((mailboxNode) => mailboxNode.item.name != null && mailboxNode.item.name?.name.isNotEmpty == true) - .map((mailboxNode) => mailboxNode.item.name!.name) + .where((mailboxNode) => mailboxNode.nameNotEmpty) + .map((mailboxNode) => mailboxNode.mailboxNameAsString) .toList(); + } else { + listMailboxNameAsStringExist = []; } } else { - final mailboxNodeLocation = defaultMailboxTree?.findNode((node) => node.item.id == selectedMailbox.value!.id) - ?? folderMailboxTree?.findNode((node) => node.item.id == selectedMailbox.value!.id); + final mailboxNodeLocation = _findMailboxNodeById(selectedMailbox.value!.id); if (mailboxNodeLocation != null && mailboxNodeLocation.childrenItems?.isNotEmpty == true) { final allChildrenAtMailboxLocation = mailboxNodeLocation.childrenItems!; listMailboxNameAsStringExist = allChildrenAtMailboxLocation - .where((mailboxNode) => mailboxNode.item.name != null && mailboxNode.item.name?.name.isNotEmpty == true) - .map((mailboxNode) => mailboxNode.item.name!.name) + .where((mailboxNode) => mailboxNode.nameNotEmpty) + .map((mailboxNode) => mailboxNode.mailboxNameAsString) .toList(); + } else { + listMailboxNameAsStringExist = []; } } diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 5ac6cbe41..72975b0fd 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-21T10:04:34.240729", + "@@last_modified": "2022-10-26T13:43:22.314355", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -2383,5 +2383,15 @@ "placeholders": { "numberOfConversation": {} } + }, + "messageConfirmationDialogDeleteMultipleMailbox": "{numberOfMailbox} mailbox and all of the sub-folders and messages it contains will be deleted and won't be able to recover. Do you want to continue to delete?", + "@messageConfirmationDialogDeleteMultipleMailbox": { + "type": "text", + "placeholders_order": [ + "numberOfMailbox" + ], + "placeholders": { + "numberOfMailbox": {} + } } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index be5e2aa44..e78095a74 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -2455,4 +2455,12 @@ class AppLocalizations { args: [numberOfConversation] ); } + + String messageConfirmationDialogDeleteMultipleMailbox(int numberOfMailbox) { + return Intl.message( + '$numberOfMailbox mailbox and all of the sub-folders and messages it contains will be deleted and won\'t be able to recover. Do you want to continue to delete?', + name: 'messageConfirmationDialogDeleteMultipleMailbox', + args: [numberOfMailbox] + ); + } } \ No newline at end of file