TF-314 Implement delete multiple email in ThreadView

This commit is contained in:
dab246
2022-03-30 17:45:43 +07:00
committed by Dat H. Pham
parent 8bf80db4de
commit 641dce4905
10 changed files with 239 additions and 48 deletions
@@ -326,7 +326,7 @@ class EmailController extends BaseController {
}
}
void moveToMailboxAction(PresentationEmail email) async {
void moveToMailboxAction(BuildContext context, PresentationEmail email) async {
final currentMailbox = mailboxDashBoardController.selectedMailbox.value;
final accountId = mailboxDashBoardController.accountId.value;
@@ -337,12 +337,20 @@ class EmailController extends BaseController {
);
if (destinationMailbox != null && destinationMailbox is PresentationMailbox) {
_moveToMailbox(accountId, MoveRequest(
[email.id],
currentMailbox.id,
destinationMailbox.id,
MoveAction.moveTo,
destinationPath: destinationMailbox.mailboxPath));
if (destinationMailbox.role == PresentationMailbox.roleTrash) {
_moveToTrash(context, accountId, MoveToTrashRequest(
[email.id],
currentMailbox.id,
destinationMailbox.id,
MoveAction.moveToTrash));
} else {
_moveToMailbox(accountId, MoveRequest(
[email.id],
currentMailbox.id,
destinationMailbox.id,
MoveAction.moveTo,
destinationPath: destinationMailbox.mailboxPath));
}
}
}
}
@@ -429,7 +437,7 @@ class EmailController extends BaseController {
markAsStarEmail(presentationEmail, MarkStarAction.unMarkStar);
break;
case EmailActionType.move:
moveToMailboxAction(presentationEmail);
moveToMailboxAction(context, presentationEmail);
break;
case EmailActionType.moveToTrash:
moveToTrashAction(context, presentationEmail);
@@ -50,6 +50,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_da
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart';
import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_mailbox_state.dart';
import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_trash_state.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/search_state.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/search_status.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -121,6 +122,10 @@ class MailboxController extends BaseMailboxController {
|| success is MoveMultipleEmailToMailboxHasSomeEmailFailure) {
mailboxDashBoardController.clearState();
refreshMailboxChanges();
} else if (success is MoveMultipleEmailToTrashAllSuccess
|| success is MoveMultipleEmailToTrashHasSomeEmailFailure) {
mailboxDashBoardController.clearState();
refreshMailboxChanges();
} else if (success is MarkAsEmailReadSuccess
|| success is MoveToMailboxSuccess
|| success is MoveToTrashSuccess
@@ -263,7 +263,7 @@ class MailboxDashBoardController extends ReloadableController {
currentOverlayContext!,
AppLocalizations.of(currentContext!).moved_to_trash,
AppLocalizations.of(currentContext!).undo_action,
() {
() {
final newMoveRequest = MoveRequest(
[success.emailId],
success.trashMailboxId,
@@ -0,0 +1,67 @@
import 'package:core/core.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart';
class MoveMultipleEmailToTrashAllSuccess extends UIState {
final List<EmailId> movedListEmailId;
final MailboxId currentMailboxId;
final MailboxId trashMailboxId;
final MoveAction moveAction;
MoveMultipleEmailToTrashAllSuccess(
this.movedListEmailId,
this.currentMailboxId,
this.trashMailboxId,
this.moveAction,
);
@override
List<Object?> get props => [
movedListEmailId,
currentMailboxId,
trashMailboxId,
moveAction,
];
}
class MoveMultipleEmailToTrashAllFailure extends FeatureFailure {
final MoveAction moveAction;
MoveMultipleEmailToTrashAllFailure(this.moveAction);
@override
List<Object> get props => [moveAction];
}
class MoveMultipleEmailToTrashHasSomeEmailFailure extends UIState {
final List<EmailId> movedListEmailId;
final MailboxId currentMailboxId;
final MailboxId trashMailboxId;
final MoveAction moveAction;
MoveMultipleEmailToTrashHasSomeEmailFailure(
this.movedListEmailId,
this.currentMailboxId,
this.trashMailboxId,
this.moveAction,
);
@override
List<Object?> get props => [
movedListEmailId,
currentMailboxId,
trashMailboxId,
moveAction,
];
}
class MoveMultipleEmailToTrashFailure extends FeatureFailure {
final exception;
final MoveAction moveAction;
MoveMultipleEmailToTrashFailure(this.exception, this.moveAction);
@override
List<Object> get props => [exception, moveAction];
}
@@ -0,0 +1,38 @@
import 'dart:async';
import 'package:core/core.dart';
import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_to_trash_request.dart';
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_trash_state.dart';
class MoveMultipleEmailToTrashInteractor {
final EmailRepository _emailRepository;
MoveMultipleEmailToTrashInteractor(this._emailRepository);
Stream<Either<Failure, Success>> execute(AccountId accountId, MoveToTrashRequest moveRequest) async* {
try {
final result = await _emailRepository.moveToTrash(accountId, moveRequest);
if (moveRequest.emailIds.length == result.length) {
yield Right(MoveMultipleEmailToTrashAllSuccess(
result,
moveRequest.currentMailboxId,
moveRequest.trashMailboxId,
moveRequest.moveAction));
} else if (result.isEmpty) {
yield Left(MoveMultipleEmailToTrashAllFailure(moveRequest.moveAction));
} else {
yield Right(MoveMultipleEmailToTrashHasSomeEmailFailure(
result,
moveRequest.currentMailboxId,
moveRequest.trashMailboxId,
moveRequest.moveAction));
}
} catch (e) {
yield Left(MoveMultipleEmailToTrashFailure(e, moveRequest.moveAction));
}
}
}
@@ -27,6 +27,7 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/load_more_emails_i
import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_trash_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/refresh_all_emails_in_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/search_email_interactor.dart';
@@ -53,6 +54,7 @@ class ThreadBindings extends BaseBindings {
Get.find<LoadMoreEmailsInMailboxInteractor>(),
Get.find<SearchEmailInteractor>(),
Get.find<SearchMoreEmailInteractor>(),
Get.find<MoveMultipleEmailToTrashInteractor>(),
));
}
@@ -90,6 +92,7 @@ class ThreadBindings extends BaseBindings {
Get.lazyPut(() => LoadMoreEmailsInMailboxInteractor(Get.find<ThreadRepository>()));
Get.lazyPut(() => SearchEmailInteractor(Get.find<ThreadRepository>()));
Get.lazyPut(() => SearchMoreEmailInteractor(Get.find<ThreadRepository>()));
Get.lazyPut(() => MoveMultipleEmailToTrashInteractor(Get.find<EmailRepository>()));
}
@override
@@ -20,6 +20,7 @@ import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.da
import 'package:tmail_ui_user/features/composer/domain/state/update_email_drafts_state.dart';
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/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';
@@ -37,6 +38,7 @@ import 'package:tmail_ui_user/features/thread/domain/state/load_more_emails_stat
import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart';
import 'package:tmail_ui_user/features/thread/domain/state/mark_as_star_multiple_email_state.dart';
import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_mailbox_state.dart';
import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_trash_state.dart';
import 'package:tmail_ui_user/features/thread/domain/state/refresh_changes_all_email_state.dart';
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';
@@ -45,6 +47,7 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/load_more_emails_i
import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_trash_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/refresh_all_emails_in_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/usecases/search_email_interactor.dart';
@@ -72,6 +75,7 @@ class ThreadController extends BaseController {
final LoadMoreEmailsInMailboxInteractor _loadMoreEmailsInMailboxInteractor;
final SearchEmailInteractor _searchEmailInteractor;
final SearchMoreEmailInteractor _searchMoreEmailInteractor;
final MoveMultipleEmailToTrashInteractor _moveMultipleEmailToTrashInteractor;
final emailList = <PresentationEmail>[].obs;
final emailListSearch = <PresentationEmail>[].obs;
@@ -105,6 +109,7 @@ class ThreadController extends BaseController {
this._loadMoreEmailsInMailboxInteractor,
this._searchEmailInteractor,
this._searchMoreEmailInteractor,
this._moveMultipleEmailToTrashInteractor,
);
@override
@@ -214,6 +219,9 @@ class ThreadController extends BaseController {
} else if (success is MarkAsStarMultipleEmailAllSuccess
|| success is MarkAsStarMultipleEmailHasSomeEmailFailure) {
_markAsStarMultipleEmailSuccess(success);
} else if (success is MoveMultipleEmailToTrashAllSuccess
|| success is MoveMultipleEmailToTrashHasSomeEmailFailure) {
_moveSelectedMultipleEmailToTrashSuccess(success);
}
}
);
@@ -443,11 +451,10 @@ class ThreadController extends BaseController {
}
void markAsReadSelectedMultipleEmail(List<PresentationEmail> listPresentationEmail) {
cancelSelectEmail();
final readAction = listPresentationEmail.isAllEmailRead ? ReadActions.markAsUnread : ReadActions.markAsRead;
final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value;
if (_accountId != null && mailboxCurrent != null) {
cancelSelectEmail();
final listEmail = listPresentationEmail.map((presentationEmail) => presentationEmail.toEmail()).toList();
consumeState(_markAsMultipleEmailReadInteractor.execute(_accountId!, listEmail, readAction));
}
@@ -521,10 +528,6 @@ class ThreadController extends BaseController {
void moveSelectedMultipleEmailToMailboxAction(List<PresentationEmail> listEmail) async {
final currentMailbox = mailboxDashBoardController.selectedMailbox.value;
if (currentMailbox != null && _accountId != null) {
popBack();
cancelSelectEmail();
final listEmailIds = listEmail.map((email) => email.id).toList();
final destinationMailbox = await push(
AppRoutes.DESTINATION_PICKER,
@@ -532,19 +535,28 @@ class ThreadController extends BaseController {
);
if (destinationMailbox != null && destinationMailbox is PresentationMailbox) {
_moveSelectedEmailMultipleToMailbox(
_accountId!,
MoveRequest(
if (destinationMailbox.role == PresentationMailbox.roleTrash) {
_moveSelectedEmailMultipleToTrash(_accountId!, MoveToTrashRequest(
listEmailIds,
currentMailbox.id,
destinationMailbox.id,
MoveAction.moveTo,
destinationPath: destinationMailbox.mailboxPath));
MoveAction.moveToTrash));
} else {
_moveSelectedEmailMultipleToMailbox(
_accountId!,
MoveRequest(
listEmailIds,
currentMailbox.id,
destinationMailbox.id,
MoveAction.moveTo,
destinationPath: destinationMailbox.mailboxPath));
}
}
}
}
void _moveSelectedEmailMultipleToMailbox(AccountId accountId, MoveRequest moveRequest) {
cancelSelectEmail();
consumeState(_moveMultipleEmailToMailboxInteractor.execute(accountId, moveRequest));
}
@@ -601,6 +613,66 @@ class ThreadController extends BaseController {
}
}
void moveSelectedMultipleEmailToTrashAction(List<PresentationEmail> listEmail) async {
final currentMailbox = mailboxDashBoardController.selectedMailbox.value;
final trashMailboxId = mailboxDashBoardController.mapDefaultMailboxId[PresentationMailbox.roleTrash];
if (currentMailbox != null && _accountId != null && trashMailboxId != null) {
final listEmailIds = listEmail.map((email) => email.id).toList();
_moveSelectedEmailMultipleToTrash(
_accountId!,
MoveToTrashRequest(listEmailIds, currentMailbox.id, trashMailboxId, MoveAction.moveToTrash)
);
}
}
void _moveSelectedEmailMultipleToTrash(AccountId accountId, MoveToTrashRequest moveRequest) {
cancelSelectEmail();
consumeState(_moveMultipleEmailToTrashInteractor.execute(accountId, moveRequest));
}
void _moveSelectedMultipleEmailToTrashSuccess(Success success) {
mailboxDashBoardController.dispatchState(Right(success));
List<EmailId> movedEmailIds = [];
MailboxId? currentMailboxId;
MailboxId? trashMailboxId;
MoveAction? moveAction;
if (success is MoveMultipleEmailToTrashAllSuccess) {
movedEmailIds = success.movedListEmailId;
currentMailboxId = success.currentMailboxId;
trashMailboxId = success.trashMailboxId;
moveAction = success.moveAction;
} else if (success is MoveMultipleEmailToTrashHasSomeEmailFailure) {
movedEmailIds = success.movedListEmailId;
currentMailboxId = success.currentMailboxId;
trashMailboxId = success.trashMailboxId;
moveAction = success.moveAction;
}
if (currentContext != null && currentOverlayContext != null && moveAction == MoveAction.moveToTrash) {
_appToast.showToastWithAction(
currentOverlayContext!,
AppLocalizations.of(currentContext!).moved_to_trash,
AppLocalizations.of(currentContext!).undo_action,
() {
if (trashMailboxId != null && currentMailboxId != null) {
_revertedToOriginalMailbox(MoveRequest(movedEmailIds, trashMailboxId, currentMailboxId, MoveAction.undo));
}
}
);
}
_refreshEmailChanges();
}
void _revertedToOriginalMailbox(MoveRequest newMoveRequest) {
if (_accountId != null) {
consumeState(_moveMultipleEmailToMailboxInteractor.execute(_accountId!, newMoveRequest));
}
}
void markAsStarEmail(PresentationEmail presentationEmail) {
final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value;
if (_accountId != null && mailboxCurrent != null) {
@@ -614,11 +686,10 @@ class ThreadController extends BaseController {
}
void markAsStarSelectedMultipleEmail(List<PresentationEmail> listPresentationEmail) {
cancelSelectEmail();
final starAction = listPresentationEmail.isAllEmailStarred ? MarkStarAction.unMarkStar : MarkStarAction.markStar;
final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value;
if (_accountId != null && mailboxCurrent != null) {
cancelSelectEmail();
final listEmail = listPresentationEmail.map((presentationEmail) => presentationEmail.toEmail()).toList();
consumeState(_markAsStarMultipleEmailInteractor.execute(_accountId!, listEmail, starAction));
}
@@ -730,10 +801,8 @@ class ThreadController extends BaseController {
case EmailActionType.move:
moveSelectedMultipleEmailToMailboxAction(selectionEmail);
break;
case EmailActionType.markAsFlag:
case EmailActionType.markAsSpam:
case EmailActionType.delete:
_appToast.showToast(AppLocalizations.of(context).the_feature_is_under_development);
case EmailActionType.moveToTrash:
moveSelectedMultipleEmailToTrashAction(selectionEmail);
break;
default:
break;
@@ -207,17 +207,27 @@ class ThreadView extends GetWidget<ThreadController> 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)),
]);
}
Widget _buildListButtonSelectionForMobile(BuildContext context) {
return Obx(() {
if (controller.isSelectionEnabled() && !_responsiveUtils.isDesktop(context)) {
if (controller.isSelectionEnabled() && !_responsiveUtils.isDesktop(context) && controller.listEmailSelected.isNotEmpty) {
return Column(children: [
Divider(color: AppColor.lineItemListColor, height: 1, thickness: 0.2),
Padding(
padding: EdgeInsets.all(10),
child: (BottomBarThreadSelectionWidget(context, _imagePaths, _responsiveUtils, controller.listEmailSelected)
child: (BottomBarThreadSelectionWidget(
context,
_imagePaths,
_responsiveUtils,
controller.listEmailSelected,
controller.mailboxDashBoardController.selectedMailbox.value)
..addOnPressEmailSelectionActionClick((actionType, selectionEmail) => controller.pressEmailSelectionAction(context, actionType, selectionEmail)))
.build()),
]);
@@ -11,6 +11,7 @@ class BottomBarThreadSelectionWidget {
final ImagePaths _imagePaths;
final ResponsiveUtils _responsiveUtils;
final List<PresentationEmail> _listSelectionEmail;
final PresentationMailbox? _currentMailbox;
OnPressEmailSelectionActionClick? _onPressEmailSelectionActionClick;
@@ -19,6 +20,7 @@ class BottomBarThreadSelectionWidget {
this._imagePaths,
this._responsiveUtils,
this._listSelectionEmail,
this._currentMailbox,
);
void addOnPressEmailSelectionActionClick(OnPressEmailSelectionActionClick onPressEmailSelectionActionClick) {
@@ -56,15 +58,18 @@ class BottomBarThreadSelectionWidget {
_listSelectionEmail.isAllEmailRead ? AppLocalizations.of(_context).unread : AppLocalizations.of(_context).read,
isVertical: _responsiveUtils.isMobile(_context)))
.build(),
(ButtonBuilder(_imagePaths.icFlag)
..key(Key('button_flag_email'))
(ButtonBuilder(_listSelectionEmail.isAllEmailStarred ? _imagePaths.icUnStar : _imagePaths.icStar)
..key(Key('button_mark_as_star_email'))
..paddingIcon(EdgeInsets.all(8))
..textStyle(TextStyle(fontSize: 12, color: AppColor.colorTextButton))
..onPressActionClick(() {
if (_onPressEmailSelectionActionClick != null) {
_onPressEmailSelectionActionClick!(EmailActionType.markAsFlag, _listSelectionEmail);
_onPressEmailSelectionActionClick!(
_listSelectionEmail.isAllEmailStarred ? EmailActionType.markAsUnStar : EmailActionType.markAsStar,
_listSelectionEmail);
}})
..text(AppLocalizations.of(_context).flag, isVertical: _responsiveUtils.isMobile(_context)))
..text(_listSelectionEmail.isAllEmailStarred ? AppLocalizations.of(_context).mark_as_unstar : AppLocalizations.of(_context).mark_as_star,
isVertical: _responsiveUtils.isMobile(_context)))
.build(),
(ButtonBuilder(_imagePaths.icMove)
..key(Key('button_move_email'))
@@ -76,25 +81,14 @@ class BottomBarThreadSelectionWidget {
}})
..text(AppLocalizations.of(_context).move, isVertical: _responsiveUtils.isMobile(_context)))
.build(),
if (!_responsiveUtils.isDesktop(_context) && !_responsiveUtils.isTabletLarge(_context))
(ButtonBuilder(_imagePaths.icSpam)
..key(Key('button_spam_email'))
..paddingIcon(EdgeInsets.all(8))
..textStyle(TextStyle(fontSize: 12, color: AppColor.colorTextButton))
..onPressActionClick(() {
if (_onPressEmailSelectionActionClick != null) {
_onPressEmailSelectionActionClick!(EmailActionType.markAsSpam, _listSelectionEmail);
}})
..text(AppLocalizations.of(_context).spam, isVertical: _responsiveUtils.isMobile(_context)))
.build(),
if (!_responsiveUtils.isDesktop(_context) && !_responsiveUtils.isTabletLarge(_context))
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.delete, _listSelectionEmail);
_onPressEmailSelectionActionClick!(EmailActionType.moveToTrash, _listSelectionEmail);
}})
..text(AppLocalizations.of(_context).delete, isVertical: _responsiveUtils.isMobile(_context)))
.build()
-3
View File
@@ -6,12 +6,9 @@ enum EmailActionType {
compose,
markAsRead,
markAsUnread,
markAsFlag,
markAsStar,
markAsUnStar,
move,
markAsSpam,
delete,
edit,
composeFromEmailAddress,
moveToTrash,