From 6445e3076f2a43640d54f815c9df5bc560cbc140 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 5 Apr 2022 11:00:18 +0700 Subject: [PATCH] TF-239 Delete multiple emails permanently --- assets/images/ic_delete_trash.svg | 3 + .../presentation/resources/image_paths.dart | 1 + core/lib/presentation/utils/app_toast.dart | 10 ++- .../data/datasource/email_datasource.dart | 3 + .../email_datasource_impl.dart | 10 +++ .../email/data/network/email_api.dart | 49 +++++++++++ .../repository/email_repository_impl.dart | 6 ++ .../domain/repository/email_repository.dart | 3 + .../state/delete_multiple_email_state.dart | 40 +++++++++ ...ultiple_emails_permanently_interactor.dart | 28 ++++++ .../presentation/mailbox_controller.dart | 5 ++ .../model/delete_action_type.dart | 45 ++++++++++ .../thread/presentation/thread_bindings.dart | 3 + .../presentation/thread_controller.dart | 85 +++++++++++++++++++ .../thread/presentation/thread_view.dart | 14 +-- .../bottom_bar_thread_selection_widget.dart | 24 +++--- lib/main/localizations/app_localizations.dart | 72 ++++++++++++++++ model/lib/email/email_action_type.dart | 1 + 18 files changed, 382 insertions(+), 20 deletions(-) create mode 100644 assets/images/ic_delete_trash.svg create mode 100644 lib/features/email/domain/state/delete_multiple_email_state.dart create mode 100644 lib/features/email/domain/usecases/delete_multiple_emails_permanently_interactor.dart create mode 100644 lib/features/thread/presentation/model/delete_action_type.dart diff --git a/assets/images/ic_delete_trash.svg b/assets/images/ic_delete_trash.svg new file mode 100644 index 000000000..341e41e7a --- /dev/null +++ b/assets/images/ic_delete_trash.svg @@ -0,0 +1,3 @@ + + + diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index cd78ffc17..1dcd28a5e 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -87,6 +87,7 @@ class ImagePaths { String get icSendToastError => _getImagePath('ic_send_toast_error.svg'); String get icEmpty => _getImagePath('ic_empty.svg'); String get icNotConnection => _getImagePath('ic_not_connection.svg'); + String get icDeleteTrash => _getImagePath('ic_delete_trash.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/core/lib/presentation/utils/app_toast.dart b/core/lib/presentation/utils/app_toast.dart index f3301446a..e3870b495 100644 --- a/core/lib/presentation/utils/app_toast.dart +++ b/core/lib/presentation/utils/app_toast.dart @@ -61,9 +61,11 @@ class AppToast { ); } - void showToastWithIcon(BuildContext context, - {String? message, String? icon, Color? bgColor, - Color? textColor, double? radius, EdgeInsets? padding, TextStyle? textStyle}) { + void showToastWithIcon(BuildContext context, { + String? message, String? icon, Color? bgColor, + Color? textColor, double? radius, EdgeInsets? padding, + TextStyle? textStyle, double? widthToast}) { + double sizeWidth = context.width > 360 ? 360 : context.width - 40; final toast = Material( color: bgColor ?? Colors.white, elevation: 10, @@ -75,7 +77,7 @@ class AppToast { borderRadius: BorderRadius.circular(radius ?? 10.0), color: bgColor ?? Colors.white, ), - width: 320, + width: widthToast ?? sizeWidth, child: Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/features/email/data/datasource/email_datasource.dart b/lib/features/email/data/datasource/email_datasource.dart index cd6b0484e..08e4aafbc 100644 --- a/lib/features/email/data/datasource/email_datasource.dart +++ b/lib/features/email/data/datasource/email_datasource.dart @@ -1,5 +1,6 @@ import 'package:dio/dio.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; @@ -50,4 +51,6 @@ abstract class EmailDataSource { Future removeEmailDrafts(AccountId accountId, EmailId emailId); Future updateEmailDrafts(AccountId accountId, Email newEmail, EmailId oldEmailId); + + Future> deleteMultipleEmailPermanently(Session session, AccountId accountId, List emailIds); } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/email_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_datasource_impl.dart index 6a3b72004..4d7f7ca8e 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -1,5 +1,6 @@ import 'package:dio/dio.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; @@ -132,4 +133,13 @@ class EmailDataSourceImpl extends EmailDataSource { throw error; }); } + + @override + Future> deleteMultipleEmailPermanently(Session session, AccountId accountId, List emailIds) { + return Future.sync(() async { + return await emailAPI.deleteMultipleEmailPermanently(session, accountId, emailIds); + }).catchError((error) { + throw error; + }); + } } \ No newline at end of file diff --git a/lib/features/email/data/network/email_api.dart b/lib/features/email/data/network/email_api.dart index d27c988f2..fe6dc8096 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -7,10 +7,13 @@ import 'package:external_path/external_path.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:jmap_dart_client/http/http_client.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/capability/core_capability.dart'; import 'package:jmap_dart_client/jmap/core/patch_object.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; import 'package:jmap_dart_client/jmap/core/reference_id.dart'; import 'package:jmap_dart_client/jmap/core/reference_prefix.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/jmap_request.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/get/get_email_method.dart'; @@ -28,6 +31,7 @@ import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart' import 'package:model/model.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_trash_request.dart'; +import 'package:tmail_ui_user/main/error/capability_validator.dart'; class EmailAPI { @@ -381,4 +385,49 @@ class EmailAPI { throw error; }); } + + Future> deleteMultipleEmailPermanently(Session session, AccountId accountId, List emailIds) async { + requireCapability(session, [CapabilityIdentifier.jmapCore, CapabilityIdentifier.jmapMail]); + + final coreCapability = (session.capabilities[CapabilityIdentifier.jmapCore] as CoreCapability); + final maxMethodCount = coreCapability.maxCallsInRequest.value.toInt(); + + List listEmailResult = []; + var start = 0; + var end = 0; + while (end < emailIds.length) { + start = end; + if (emailIds.length - start >= maxMethodCount) { + end = maxMethodCount; + } else { + end = emailIds.length; + } + log('EmailAPI::deleteMultipleEmailPermanently(): delete from $start to $end / ${emailIds.length}'); + final currentExecuteList = emailIds.sublist(start, end); + + final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation()); + final currentSetEmailInvocations = currentExecuteList + .map((emailId) { + return SetEmailMethod(accountId) + ..addDestroy(Set.of([emailId.id])); + }) + .map(requestBuilder.invocation) + .toList(); + + final response = await (requestBuilder + ..usings(Set.of([CapabilityIdentifier.jmapCore, CapabilityIdentifier.jmapMail]))) + .build() + .execute(); + + currentSetEmailInvocations + .map((currentInvocation) => response.parse(currentInvocation.methodCallId, SetEmailResponse.deserialize)) + .map((response) => response?.destroyed?.map((id) => EmailId(id))) + .where((listId) => listId != null && listId.isNotEmpty) + .forEach((listId) { + listEmailResult.addAll(listId!); + }); + } + + return listEmailResult; + } } \ No newline at end of file diff --git a/lib/features/email/data/repository/email_repository_impl.dart b/lib/features/email/data/repository/email_repository_impl.dart index 4b4529205..c79f6013a 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -1,6 +1,7 @@ import 'package:dio/dio.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; @@ -114,4 +115,9 @@ class EmailRepositoryImpl extends EmailRepository { Future> moveToTrash(AccountId accountId, MoveToTrashRequest moveRequest) { return emailDataSource.moveToTrash(accountId, moveRequest); } + + @override + Future> deleteMultipleEmailPermanently(Session session, AccountId accountId, List emailIds) { + return emailDataSource.deleteMultipleEmailPermanently(session, accountId, emailIds); + } } \ No newline at end of file diff --git a/lib/features/email/domain/repository/email_repository.dart b/lib/features/email/domain/repository/email_repository.dart index fe4c6ac9d..343c613a4 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -1,5 +1,6 @@ import 'package:dio/dio.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; @@ -57,4 +58,6 @@ abstract class EmailRepository { Future removeEmailDrafts(AccountId accountId, EmailId emailId); Future updateEmailDrafts(AccountId accountId, Email newEmail, EmailId oldEmailId); + + Future> deleteMultipleEmailPermanently(Session session, AccountId accountId, List emailIds); } \ No newline at end of file diff --git a/lib/features/email/domain/state/delete_multiple_email_state.dart b/lib/features/email/domain/state/delete_multiple_email_state.dart new file mode 100644 index 000000000..3fdbdb9bf --- /dev/null +++ b/lib/features/email/domain/state/delete_multiple_email_state.dart @@ -0,0 +1,40 @@ +import 'package:core/core.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; + +class DeleteMultipleEmailsPermanentlyAllSuccess extends UIState { + + List emailIds; + + DeleteMultipleEmailsPermanentlyAllSuccess(this.emailIds); + + @override + List get props => [emailIds]; +} + +class DeleteMultipleEmailsPermanentlyHasSomeEmailFailure extends UIState { + + List emailIds; + + DeleteMultipleEmailsPermanentlyHasSomeEmailFailure(this.emailIds); + + @override + List get props => [emailIds]; +} + +class DeleteMultipleEmailsPermanentlyAllFailure extends FeatureFailure { + + DeleteMultipleEmailsPermanentlyAllFailure(); + + @override + List get props => []; +} + +class DeleteMultipleEmailsPermanentlyFailure extends FeatureFailure { + + final exception; + + DeleteMultipleEmailsPermanentlyFailure(this.exception); + + @override + List get props => [exception]; +} \ No newline at end of file diff --git a/lib/features/email/domain/usecases/delete_multiple_emails_permanently_interactor.dart b/lib/features/email/domain/usecases/delete_multiple_emails_permanently_interactor.dart new file mode 100644 index 000000000..c267492c0 --- /dev/null +++ b/lib/features/email/domain/usecases/delete_multiple_emails_permanently_interactor.dart @@ -0,0 +1,28 @@ +import 'package:core/core.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'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; +import 'package:tmail_ui_user/features/email/domain/state/delete_multiple_email_state.dart'; + +class DeleteMultipleEmailsPermanentlyInteractor { + final EmailRepository emailRepository; + + DeleteMultipleEmailsPermanentlyInteractor(this.emailRepository); + + Stream> execute(Session session, AccountId accountId, List emailIds) async* { + try { + final listResult = await emailRepository.deleteMultipleEmailPermanently(session, accountId, emailIds); + if (listResult.length == emailIds.length) { + yield Right(DeleteMultipleEmailsPermanentlyAllSuccess(listResult)); + } else if (listResult.isNotEmpty) { + yield Right(DeleteMultipleEmailsPermanentlyHasSomeEmailFailure(listResult)); + } else { + yield Left(DeleteMultipleEmailsPermanentlyAllFailure()); + } + } catch (e) { + yield Left(DeleteMultipleEmailsPermanentlyFailure(e)); + } + } +} \ 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 6f4e96c44..8eed5d074 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -16,6 +16,7 @@ import 'package:tmail_ui_user/features/base/base_mailbox_controller.dart'; import 'package:tmail_ui_user/features/composer/domain/state/save_email_as_drafts_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/update_email_drafts_state.dart'; +import 'package:tmail_ui_user/features/email/domain/state/delete_multiple_email_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/move_to_trash_state.dart'; @@ -126,6 +127,10 @@ class MailboxController extends BaseMailboxController { || success is MoveMultipleEmailToTrashHasSomeEmailFailure) { mailboxDashBoardController.clearState(); refreshMailboxChanges(); + } else if (success is DeleteMultipleEmailsPermanentlyAllSuccess + || success is DeleteMultipleEmailsPermanentlyHasSomeEmailFailure) { + mailboxDashBoardController.clearState(); + refreshMailboxChanges(); } else if (success is MarkAsEmailReadSuccess || success is MoveToMailboxSuccess || success is MoveToTrashSuccess diff --git a/lib/features/thread/presentation/model/delete_action_type.dart b/lib/features/thread/presentation/model/delete_action_type.dart new file mode 100644 index 000000000..78cc5b1da --- /dev/null +++ b/lib/features/thread/presentation/model/delete_action_type.dart @@ -0,0 +1,45 @@ + +import 'package:flutter/cupertino.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +enum DeleteActionType { + all, + multiple, + single, +} + +extension DeleteActionTypeExtension on DeleteActionType { + + String getTitleDialog(BuildContext context) { + switch(this) { + case DeleteActionType.all: + return AppLocalizations.of(context).empty_trash_folder; + case DeleteActionType.multiple: + return AppLocalizations.of(context).delete_messages_forever; + case DeleteActionType.single: + return AppLocalizations.of(context).delete_message_forever; + } + } + + String getContentDialog(BuildContext context, {int? count}) { + switch(this) { + case DeleteActionType.all: + return AppLocalizations.of(context).empty_trash_dialog_message; + case DeleteActionType.multiple: + return AppLocalizations.of(context).delete_multiple_messages_dialog(count ?? 0); + case DeleteActionType.single: + return AppLocalizations.of(context).delete_single_message_dialog; + } + } + + String getConfirmActionName(BuildContext context) { + switch(this) { + case DeleteActionType.all: + return AppLocalizations.of(context).delete_all; + case DeleteActionType.multiple: + return AppLocalizations.of(context).delete; + case DeleteActionType.single: + return AppLocalizations.of(context).delete; + } + } +} \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_bindings.dart b/lib/features/thread/presentation/thread_bindings.dart index 07fd9c4d5..7e084d863 100644 --- a/lib/features/thread/presentation/thread_bindings.dart +++ b/lib/features/thread/presentation/thread_bindings.dart @@ -10,6 +10,7 @@ import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/delete_multiple_emails_permanently_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart'; @@ -55,6 +56,7 @@ class ThreadBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), + Get.find(), )); } @@ -93,6 +95,7 @@ class ThreadBindings extends BaseBindings { Get.lazyPut(() => SearchEmailInteractor(Get.find())); Get.lazyPut(() => SearchMoreEmailInteractor(Get.find())); Get.lazyPut(() => MoveMultipleEmailToTrashInteractor(Get.find())); + Get.lazyPut(() => DeleteMultipleEmailsPermanentlyInteractor(Get.find())); } @override diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index fe2801903..b416158d4 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -2,6 +2,7 @@ import 'package:core/core.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/sort/comparator.dart'; @@ -14,6 +15,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; +import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/composer/domain/state/save_email_as_drafts_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart'; @@ -21,10 +23,12 @@ import 'package:tmail_ui_user/features/composer/domain/state/update_email_drafts import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_trash_request.dart'; +import 'package:tmail_ui_user/features/email/domain/state/delete_multiple_email_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_star_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/move_to_trash_state.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/delete_multiple_emails_permanently_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; @@ -53,6 +57,7 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/refresh_changes_em import 'package:tmail_ui_user/features/thread/domain/usecases/search_email_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/search_more_email_interactor.dart'; import 'package:tmail_ui_user/features/thread/presentation/extensions/filter_message_option_extension.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/search_status.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; @@ -76,6 +81,7 @@ class ThreadController extends BaseController { final SearchEmailInteractor _searchEmailInteractor; final SearchMoreEmailInteractor _searchMoreEmailInteractor; final MoveMultipleEmailToTrashInteractor _moveMultipleEmailToTrashInteractor; + final DeleteMultipleEmailsPermanentlyInteractor _deleteMultipleEmailsPermanentlyInteractor; final emailList = [].obs; final emailListSearch = [].obs; @@ -110,6 +116,7 @@ class ThreadController extends BaseController { this._searchEmailInteractor, this._searchMoreEmailInteractor, this._moveMultipleEmailToTrashInteractor, + this._deleteMultipleEmailsPermanentlyInteractor, ); @override @@ -222,6 +229,9 @@ class ThreadController extends BaseController { } else if (success is MoveMultipleEmailToTrashAllSuccess || success is MoveMultipleEmailToTrashHasSomeEmailFailure) { _moveSelectedMultipleEmailToTrashSuccess(success); + } else if (success is DeleteMultipleEmailsPermanentlyAllSuccess + || success is DeleteMultipleEmailsPermanentlyHasSomeEmailFailure) { + _deleteMultipleEmailsPermanentlySuccess(success); } } ); @@ -804,11 +814,86 @@ class ThreadController extends BaseController { case EmailActionType.moveToTrash: moveSelectedMultipleEmailToTrashAction(selectionEmail); break; + case EmailActionType.deletePermanently: + deleteEmailsPermanently(context, DeleteActionType.multiple, selectedEmails: selectionEmail); + break; default: break; } } + bool get isMailboxTrash => mailboxDashBoardController.selectedMailbox.value?.role == PresentationMailbox.roleTrash; + + void deleteEmailsPermanently(BuildContext context, DeleteActionType actionType, {List? selectedEmails}) { + if (_responsiveUtils.isMobile(context)) { + (ConfirmationDialogActionSheetBuilder(context) + ..messageText(actionType.getContentDialog(context, count: selectedEmails?.length)) + ..onCancelAction(AppLocalizations.of(context).cancel, () => popBack()) + ..onConfirmAction(actionType.getConfirmActionName(context), () => _deleteEmailsPermanentlyAction(actionType))) + .show(); + } else { + showDialog( + context: context, + barrierColor: AppColor.colorDefaultCupertinoActionSheet, + builder: (BuildContext context) => PointerInterceptor(child: (ConfirmDialogBuilder(_imagePaths) + ..key(Key('confirm_dialog_delete_emails_permanently')) + ..title(actionType.getTitleDialog(context)) + ..content(actionType.getContentDialog(context, count: selectedEmails?.length)) + ..addIcon(SvgPicture.asset(_imagePaths.icRemoveDialog, fit: BoxFit.fill)) + ..colorConfirmButton(AppColor.colorConfirmActionDialog) + ..styleTextConfirmButton(TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorActionDeleteConfirmDialog)) + ..onCloseButtonAction(() => popBack()) + ..onConfirmButtonAction(actionType.getConfirmActionName(context), () => _deleteEmailsPermanentlyAction(actionType)) + ..onCancelButtonAction(AppLocalizations.of(context).cancel, () => popBack())) + .build())); + } + } + + void _deleteEmailsPermanentlyAction(DeleteActionType actionType) { + popBack(); + + switch(actionType) { + case DeleteActionType.all: + break; + case DeleteActionType.multiple: + _deleteMultipleEmailsPermanently(listEmailSelected); + break; + case DeleteActionType.single: + break; + } + } + + void _deleteMultipleEmailsPermanently(List emailList) { + cancelSelectEmail(); + + final session = mailboxDashBoardController.sessionCurrent; + final listEmailIds = emailList.map((email) => email.id).toList(); + if (session != null && _accountId != null && listEmailIds.isNotEmpty) { + consumeState(_deleteMultipleEmailsPermanentlyInteractor.execute(session, _accountId!, listEmailIds)); + } + } + + void _deleteMultipleEmailsPermanentlySuccess(Success success) { + mailboxDashBoardController.dispatchState(Right(success)); + + List listEmailIdResult = []; + if (success is DeleteMultipleEmailsPermanentlyAllSuccess) { + listEmailIdResult = success.emailIds; + } else if (success is DeleteMultipleEmailsPermanentlyHasSomeEmailFailure) { + listEmailIdResult = success.emailIds; + } + + if (currentContext != null && currentOverlayContext != null && listEmailIdResult.isNotEmpty) { + _appToast.showToastWithIcon( + currentOverlayContext!, + widthToast: _responsiveUtils.isDesktop(currentContext!) ? 360 : null, + message: AppLocalizations.of(currentContext!).toast_message_delete_multiple_email_permanently_success(listEmailIdResult.length), + icon: _imagePaths.icDeleteToast); + } + + _refreshEmailChanges(); + } + void openMailboxLeftMenu() { mailboxDashBoardController.openDrawer(); } diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index c3f6949da..18bbb3c7b 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -8,6 +8,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/user_setti import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/search_more_email_state.dart'; import 'package:tmail_ui_user/features/thread/presentation/extensions/filter_message_option_extension.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart'; @@ -207,11 +208,14 @@ class ThreadView extends GetWidget with UserSettingPopupMenuMi icon: SvgPicture.asset(_imagePaths.icMove, fit: BoxFit.fill), tooltip: AppLocalizations.of(context).move, onTap: () => controller.pressEmailSelectionAction(context, EmailActionType.move, controller.listEmailSelected)), - if (controller.mailboxDashBoardController.selectedMailbox.value?.role != PresentationMailbox.roleTrash) - buildIconWeb( - icon: SvgPicture.asset(_imagePaths.icDelete, fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).move_to_trash, - onTap: () => controller.pressEmailSelectionAction(context, EmailActionType.moveToTrash, controller.listEmailSelected)), + buildIconWeb( + icon: SvgPicture.asset(_imagePaths.icDelete, fit: BoxFit.fill), + tooltip: controller.mailboxDashBoardController.selectedMailbox.value?.role != PresentationMailbox.roleTrash + ? AppLocalizations.of(context).move_to_trash + : AppLocalizations.of(context).delete_permanently, + onTap: () => controller.mailboxDashBoardController.selectedMailbox.value?.role != PresentationMailbox.roleTrash + ? controller.pressEmailSelectionAction(context, EmailActionType.moveToTrash, controller.listEmailSelected) + : controller.deleteEmailsPermanently(context, DeleteActionType.multiple, selectedEmails: controller.listEmailSelected)), ]); } diff --git a/lib/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart b/lib/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart index 09ff1dfdd..539159fe2 100644 --- a/lib/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart +++ b/lib/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart @@ -81,17 +81,19 @@ class BottomBarThreadSelectionWidget { }}) ..text(AppLocalizations.of(_context).move, isVertical: _responsiveUtils.isMobile(_context))) .build(), - if (!_responsiveUtils.isDesktop(_context) && _currentMailbox?.role != PresentationMailbox.roleTrash) - (ButtonBuilder(_imagePaths.icDelete) - ..key(Key('button_delete_email')) - ..paddingIcon(EdgeInsets.all(8)) - ..textStyle(TextStyle(fontSize: 12, color: AppColor.colorTextButton)) - ..onPressActionClick(() { - if (_onPressEmailSelectionActionClick != null) { - _onPressEmailSelectionActionClick!(EmailActionType.moveToTrash, _listSelectionEmail); - }}) - ..text(AppLocalizations.of(_context).delete, isVertical: _responsiveUtils.isMobile(_context))) - .build() + (ButtonBuilder(_imagePaths.icDelete) + ..key(Key('button_delete_email')) + ..paddingIcon(EdgeInsets.all(8)) + ..textStyle(TextStyle(fontSize: 12, color: AppColor.colorTextButton)) + ..onPressActionClick(() { + if (_currentMailbox?.role != PresentationMailbox.roleTrash) { + _onPressEmailSelectionActionClick?.call(EmailActionType.moveToTrash, _listSelectionEmail); + } else { + _onPressEmailSelectionActionClick?.call(EmailActionType.deletePermanently, _listSelectionEmail); + } + }) + ..text(AppLocalizations.of(_context).delete, isVertical: _responsiveUtils.isMobile(_context))) + .build() ] ); } diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 7af7077ad..e0b90f155 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -983,4 +983,76 @@ class AppLocalizations { 'Team - Mail', name: 'page_name'); } + + String get message_delete_all_email_in_trash_button { + return Intl.message( + 'All messages in Trash will be deleted if you reach limited storage.', + name: 'message_delete_all_email_in_trash_button'); + } + + String get empty_trash_now { + return Intl.message( + 'Empty trash now', + name: 'empty_trash_now'); + } + + String get empty_trash_folder { + return Intl.message( + 'Empty trash folder', + name: 'empty_trash_folder'); + } + + String get empty_trash_dialog_message { + return Intl.message( + 'You are about to permanently delete all items in Trash . Do you want to continue?', + name: 'empty_trash_dialog_message'); + } + + String get delete_all { + return Intl.message( + 'Delete all', + name: 'delete_all'); + } + + String toast_message_delete_multiple_email_permanently_success(int count) { + return Intl.message( + '$count Messages has been deleted forever', + name: 'toast_message_delete_multiple_email_permanently_success'); + } + + String get toast_message_delete_a_email_permanently_success { + return Intl.message( + 'Message has been deleted forever', + name: 'toast_message_delete_a_email_permanently_success'); + } + + String get delete_permanently { + return Intl.message( + 'Delete permanently', + name: 'delete_permanently'); + } + + String get delete_messages_forever { + return Intl.message( + 'Delete messages forever', + name: 'delete_messages_forever'); + } + + String get delete_message_forever { + return Intl.message( + 'Delete message forever', + name: 'delete_message_forever'); + } + + String delete_multiple_messages_dialog(int count) { + return Intl.message( + 'You are about to permanently delete $count items in Trash . Do you want to continue?', + name: 'delete_multiple_messages_dialog'); + } + + String get delete_single_message_dialog { + return Intl.message( + 'You are about to permanently delete this message. Do you want to continue?', + name: 'delete_single_message_dialog'); + } } \ No newline at end of file diff --git a/model/lib/email/email_action_type.dart b/model/lib/email/email_action_type.dart index ee868d0af..adc6955e6 100644 --- a/model/lib/email/email_action_type.dart +++ b/model/lib/email/email_action_type.dart @@ -12,4 +12,5 @@ enum EmailActionType { edit, composeFromEmailAddress, moveToTrash, + deletePermanently, } \ No newline at end of file