TF-3947 Thread Detail Thread level move & delete
This commit is contained in:
@@ -209,7 +209,7 @@ extension EmailActionTypeExtension on EmailActionType {
|
|||||||
case EmailActionType.replyToList:
|
case EmailActionType.replyToList:
|
||||||
return appLocalizations.replyToList;
|
return appLocalizations.replyToList;
|
||||||
case EmailActionType.moveToMailbox:
|
case EmailActionType.moveToMailbox:
|
||||||
return appLocalizations.move_message;
|
return appLocalizations.moveMessage;
|
||||||
case EmailActionType.markAsStarred:
|
case EmailActionType.markAsStarred:
|
||||||
return appLocalizations.starred;
|
return appLocalizations.starred;
|
||||||
case EmailActionType.unMarkAsStarred:
|
case EmailActionType.unMarkAsStarred:
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ class EmailViewAppBarWidget extends StatelessWidget {
|
|||||||
icon: _imagePaths.icMoveEmail,
|
icon: _imagePaths.icMoveEmail,
|
||||||
iconSize: EmailViewAppBarWidgetStyles.buttonIconSize,
|
iconSize: EmailViewAppBarWidgetStyles.buttonIconSize,
|
||||||
iconColor: EmailViewAppBarWidgetStyles.iconColor,
|
iconColor: EmailViewAppBarWidgetStyles.iconColor,
|
||||||
tooltipMessage: appLocalizations.move_message,
|
tooltipMessage: appLocalizations.moveMessage,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
onTapActionCallback: () => onEmailActionClick?.call(
|
onTapActionCallback: () => onEmailActionClick?.call(
|
||||||
presentationEmail,
|
presentationEmail,
|
||||||
|
|||||||
+42
-7
@@ -1613,6 +1613,33 @@ class MailboxDashBoardController extends ReloadableController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void permanentDeleteMultipleEmailInThreadDetail(
|
||||||
|
List<EmailInThreadDetailInfo> emailsInThreadDetailInfo, {
|
||||||
|
VoidCallback? onConfirm,
|
||||||
|
}) {
|
||||||
|
if (currentContext == null) return;
|
||||||
|
|
||||||
|
final mailboxContainId = emailsInThreadDetailInfo.firstOrNull?.mailboxIdContain;
|
||||||
|
final mailboxContain = mailboxContainId == null
|
||||||
|
? null
|
||||||
|
: mapMailboxById[mailboxContainId];
|
||||||
|
|
||||||
|
deleteSelectionEmailsPermanently(
|
||||||
|
currentContext!,
|
||||||
|
DeleteActionType.multiple,
|
||||||
|
listEmails: emailsInThreadDetailInfo
|
||||||
|
.map(
|
||||||
|
(email) => PresentationEmail(
|
||||||
|
id: email.emailId,
|
||||||
|
mailboxContain: mailboxContain,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
mailboxCurrent: mailboxContain,
|
||||||
|
onConfirm: onConfirm,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void deleteSelectionEmailsPermanently(
|
void deleteSelectionEmailsPermanently(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
DeleteActionType actionType,
|
DeleteActionType actionType,
|
||||||
@@ -1620,6 +1647,7 @@ class MailboxDashBoardController extends ReloadableController
|
|||||||
List<PresentationEmail>? listEmails,
|
List<PresentationEmail>? listEmails,
|
||||||
PresentationMailbox? mailboxCurrent,
|
PresentationMailbox? mailboxCurrent,
|
||||||
Function? onCancelSelectionEmail,
|
Function? onCancelSelectionEmail,
|
||||||
|
VoidCallback? onConfirm,
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (responsiveUtils.isScreenWithShortestSide(context)) {
|
if (responsiveUtils.isScreenWithShortestSide(context)) {
|
||||||
@@ -1631,10 +1659,14 @@ class MailboxDashBoardController extends ReloadableController
|
|||||||
..onCancelAction(AppLocalizations.of(context).cancel, () => popBack())
|
..onCancelAction(AppLocalizations.of(context).cancel, () => popBack())
|
||||||
..onConfirmAction(
|
..onConfirmAction(
|
||||||
actionType.getConfirmActionName(context),
|
actionType.getConfirmActionName(context),
|
||||||
() => _deleteSelectionEmailsPermanentlyAction(
|
() {
|
||||||
|
onConfirm?.call();
|
||||||
|
_deleteSelectionEmailsPermanentlyAction(
|
||||||
actionType,
|
actionType,
|
||||||
listEmails: listEmails,
|
listEmails: listEmails,
|
||||||
onCancelSelectionEmail: onCancelSelectionEmail)))
|
onCancelSelectionEmail: onCancelSelectionEmail,
|
||||||
|
);
|
||||||
|
}))
|
||||||
.show();
|
.show();
|
||||||
} else {
|
} else {
|
||||||
Get.dialog(
|
Get.dialog(
|
||||||
@@ -1649,11 +1681,14 @@ class MailboxDashBoardController extends ReloadableController
|
|||||||
),
|
),
|
||||||
confirmText: actionType.getConfirmActionName(context),
|
confirmText: actionType.getConfirmActionName(context),
|
||||||
cancelText: AppLocalizations.of(context).cancel,
|
cancelText: AppLocalizations.of(context).cancel,
|
||||||
onConfirmButtonAction: () => _deleteSelectionEmailsPermanentlyAction(
|
onConfirmButtonAction: () {
|
||||||
actionType,
|
onConfirm?.call();
|
||||||
listEmails: listEmails,
|
_deleteSelectionEmailsPermanentlyAction(
|
||||||
onCancelSelectionEmail: onCancelSelectionEmail,
|
actionType,
|
||||||
),
|
listEmails: listEmails,
|
||||||
|
onCancelSelectionEmail: onCancelSelectionEmail,
|
||||||
|
);
|
||||||
|
},
|
||||||
onCancelButtonAction: popBack,
|
onCancelButtonAction: popBack,
|
||||||
onCloseButtonAction: popBack,
|
onCloseButtonAction: popBack,
|
||||||
)),
|
)),
|
||||||
|
|||||||
+22
@@ -46,4 +46,26 @@ extension GetThreadDetailActionStatus on ThreadDetailController {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get threadDetailIsTrashed {
|
||||||
|
final trashMailboxId = getMailboxIdByRole(PresentationMailbox.roleTrash);
|
||||||
|
return emailsInThreadDetailInfo.every(
|
||||||
|
(email) {
|
||||||
|
return email.mailboxIds?[trashMailboxId] == true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get threadDetailIsDraft {
|
||||||
|
final draftMailboxId = getMailboxIdByRole(PresentationMailbox.roleDrafts);
|
||||||
|
return emailsInThreadDetailInfo.every(
|
||||||
|
(email) {
|
||||||
|
return email.mailboxIds?[draftMailboxId] == true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get threadDetailCanPermanentlyDelete {
|
||||||
|
return threadDetailIsTrashed || threadDetailIsSpam || threadDetailIsDraft;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-1
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:core/utils/platform_info.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
@@ -6,8 +7,10 @@ import 'package:model/email/mark_star_action.dart';
|
|||||||
import 'package:model/email/read_actions.dart';
|
import 'package:model/email/read_actions.dart';
|
||||||
import 'package:model/mailbox/presentation_mailbox.dart';
|
import 'package:model/mailbox/presentation_mailbox.dart';
|
||||||
import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart';
|
import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/model/context_item_email_action.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/context_item_email_action.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_email_action.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_email_action.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
|
||||||
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_open_context_menu_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_open_context_menu_extension.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/mark_as_multiple_email_read_state.dart';
|
||||||
@@ -16,10 +19,12 @@ import 'package:tmail_ui_user/features/thread_detail/presentation/extension/clos
|
|||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/get_thread_detail_action_status.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/get_thread_detail_action_status.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||||
|
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
extension OnThreadDetailActionClick on ThreadDetailController {
|
extension OnThreadDetailActionClick on ThreadDetailController {
|
||||||
void onThreadDetailActionClick(EmailActionType threadDetailActionType) {
|
Future<void> onThreadDetailActionClick(EmailActionType threadDetailActionType) async {
|
||||||
switch (threadDetailActionType) {
|
switch (threadDetailActionType) {
|
||||||
case EmailActionType.markAsRead:
|
case EmailActionType.markAsRead:
|
||||||
case EmailActionType.markAsUnread:
|
case EmailActionType.markAsUnread:
|
||||||
@@ -62,6 +67,26 @@ extension OnThreadDetailActionClick on ThreadDetailController {
|
|||||||
: MarkStarAction.unMarkStar,
|
: MarkStarAction.unMarkStar,
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
|
case EmailActionType.deletePermanently:
|
||||||
|
mailboxDashBoardController.permanentDeleteMultipleEmailInThreadDetail(
|
||||||
|
emailsInThreadDetailInfo,
|
||||||
|
onConfirm: () => closeThreadDetailAction(currentContext),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case EmailActionType.moveToMailbox:
|
||||||
|
final mailboxId = await _pickDestinationMailboxId();
|
||||||
|
if (mailboxId == null) return;
|
||||||
|
|
||||||
|
_moveToMailbox(mailboxId, threadDetailActionType);
|
||||||
|
break;
|
||||||
|
case EmailActionType.moveToTrash:
|
||||||
|
final mailboxId = mailboxDashBoardController.getMailboxIdByRole(
|
||||||
|
PresentationMailbox.roleTrash,
|
||||||
|
);
|
||||||
|
if (mailboxId == null) return;
|
||||||
|
|
||||||
|
_moveToMailbox(mailboxId, threadDetailActionType);
|
||||||
|
break;
|
||||||
case EmailActionType.archiveMessage:
|
case EmailActionType.archiveMessage:
|
||||||
final mailboxId = mailboxDashBoardController.getMailboxIdByRole(
|
final mailboxId = mailboxDashBoardController.getMailboxIdByRole(
|
||||||
PresentationMailbox.roleArchive,
|
PresentationMailbox.roleArchive,
|
||||||
@@ -103,8 +128,12 @@ extension OnThreadDetailActionClick on ThreadDetailController {
|
|||||||
threadDetailIsStarred
|
threadDetailIsStarred
|
||||||
? EmailActionType.unMarkAsStarred
|
? EmailActionType.unMarkAsStarred
|
||||||
: EmailActionType.markAsStarred,
|
: EmailActionType.markAsStarred,
|
||||||
|
EmailActionType.moveToMailbox,
|
||||||
if (!threadDetailIsArchived) EmailActionType.archiveMessage,
|
if (!threadDetailIsArchived) EmailActionType.archiveMessage,
|
||||||
threadDetailIsSpam ? EmailActionType.unSpam : EmailActionType.moveToSpam,
|
threadDetailIsSpam ? EmailActionType.unSpam : EmailActionType.moveToSpam,
|
||||||
|
threadDetailIsTrashed
|
||||||
|
? EmailActionType.deletePermanently
|
||||||
|
: EmailActionType.moveToTrash,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (position == null) {
|
if (position == null) {
|
||||||
@@ -157,4 +186,33 @@ extension OnThreadDetailActionClick on ThreadDetailController {
|
|||||||
emailActionType: emailActionType,
|
emailActionType: emailActionType,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<MailboxId?> _pickDestinationMailboxId() async {
|
||||||
|
if (session == null || accountId == null) return null;
|
||||||
|
|
||||||
|
final threadMailboxIds = emailsInThreadDetailInfo
|
||||||
|
.map((e) => e.mailboxIdContain)
|
||||||
|
.nonNulls
|
||||||
|
.toSet();
|
||||||
|
MailboxId? mailboxIdSelected;
|
||||||
|
if (threadMailboxIds.length == 1) { // All emails in the same mailbox
|
||||||
|
mailboxIdSelected = threadMailboxIds.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
final arguments = DestinationPickerArguments(
|
||||||
|
accountId!,
|
||||||
|
MailboxActions.moveEmail,
|
||||||
|
session,
|
||||||
|
mailboxIdSelected: mailboxIdSelected,
|
||||||
|
);
|
||||||
|
final destinationMailbox = PlatformInfo.isWeb
|
||||||
|
? await DialogRouter.pushGeneralDialog(
|
||||||
|
routeName: AppRoutes.destinationPicker,
|
||||||
|
arguments: arguments,
|
||||||
|
)
|
||||||
|
: await push(AppRoutes.destinationPicker, arguments: arguments);
|
||||||
|
if (destinationMailbox is! PresentationMailbox) return null;
|
||||||
|
|
||||||
|
return destinationMailbox.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -42,6 +42,7 @@ class ThreadDetailView extends GetWidget<ThreadDetailController> {
|
|||||||
threadActionReady: controller.emailsInThreadDetailInfo.isNotEmpty &&
|
threadActionReady: controller.emailsInThreadDetailInfo.isNotEmpty &&
|
||||||
controller.emailIdsPresentation.length > 1,
|
controller.emailIdsPresentation.length > 1,
|
||||||
threadDetailIsStarred: controller.threadDetailIsStarred,
|
threadDetailIsStarred: controller.threadDetailIsStarred,
|
||||||
|
threadDetailCanPermanentlyDelete: controller.threadDetailCanPermanentlyDelete,
|
||||||
onThreadActionClick: controller.onThreadDetailActionClick,
|
onThreadActionClick: controller.onThreadDetailActionClick,
|
||||||
onThreadMoreActionClick: controller.onThreadDetailMoreActionClick,
|
onThreadMoreActionClick: controller.onThreadDetailMoreActionClick,
|
||||||
optionWidgets: [
|
optionWidgets: [
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
import 'package:core/presentation/utils/responsive_utils.dart';
|
||||||
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
@@ -23,6 +24,7 @@ class ThreadDetailAppBar extends StatelessWidget {
|
|||||||
required this.threadActionReady,
|
required this.threadActionReady,
|
||||||
required this.threadDetailIsStarred,
|
required this.threadDetailIsStarred,
|
||||||
required this.isThreadDetailEnabled,
|
required this.isThreadDetailEnabled,
|
||||||
|
required this.threadDetailCanPermanentlyDelete,
|
||||||
this.mailboxContain,
|
this.mailboxContain,
|
||||||
this.optionWidgets = const [],
|
this.optionWidgets = const [],
|
||||||
this.onThreadActionClick,
|
this.onThreadActionClick,
|
||||||
@@ -36,6 +38,7 @@ class ThreadDetailAppBar extends StatelessWidget {
|
|||||||
final bool threadActionReady;
|
final bool threadActionReady;
|
||||||
final bool threadDetailIsStarred;
|
final bool threadDetailIsStarred;
|
||||||
final bool isThreadDetailEnabled;
|
final bool isThreadDetailEnabled;
|
||||||
|
final bool threadDetailCanPermanentlyDelete;
|
||||||
final PresentationMailbox? mailboxContain;
|
final PresentationMailbox? mailboxContain;
|
||||||
final List<Widget> optionWidgets;
|
final List<Widget> optionWidgets;
|
||||||
final OnThreadActionClick? onThreadActionClick;
|
final OnThreadActionClick? onThreadActionClick;
|
||||||
@@ -81,7 +84,7 @@ class ThreadDetailAppBar extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
if (_supportDisplayMailboxNameTitle(context)) backButton,
|
if (_supportDisplayMailboxNameTitle(context)) backButton,
|
||||||
if (isThreadDetailEnabled && threadActionReady)
|
if (isThreadDetailEnabled && threadActionReady) ...[
|
||||||
_ThreadDetailAppBarButton(
|
_ThreadDetailAppBarButton(
|
||||||
icon: threadDetailIsStarred
|
icon: threadDetailIsStarred
|
||||||
? imagePaths.icStar
|
? imagePaths.icStar
|
||||||
@@ -95,6 +98,26 @@ class ThreadDetailAppBar extends StatelessWidget {
|
|||||||
? (_) => onThreadActionClick?.call(EmailActionType.unMarkAsStarred)
|
? (_) => onThreadActionClick?.call(EmailActionType.unMarkAsStarred)
|
||||||
: (_) => onThreadActionClick?.call(EmailActionType.markAsStarred),
|
: (_) => onThreadActionClick?.call(EmailActionType.markAsStarred),
|
||||||
),
|
),
|
||||||
|
_ThreadDetailAppBarButton(
|
||||||
|
icon: imagePaths.icMoveEmail,
|
||||||
|
tooltipMessage: AppLocalizations.of(context).moveMessage,
|
||||||
|
responsiveUtils: responsiveUtils,
|
||||||
|
onTapActionCallback: (_) => onThreadActionClick?.call(EmailActionType.moveToMailbox),
|
||||||
|
),
|
||||||
|
_ThreadDetailAppBarButton(
|
||||||
|
icon: imagePaths.icDeleteComposer,
|
||||||
|
iconColor: threadDetailCanPermanentlyDelete
|
||||||
|
? AppColor.redFF3347
|
||||||
|
: EmailViewAppBarWidgetStyles.iconColor,
|
||||||
|
tooltipMessage: threadDetailCanPermanentlyDelete
|
||||||
|
? AppLocalizations.of(context).delete_permanently
|
||||||
|
: AppLocalizations.of(context).move_to_trash,
|
||||||
|
responsiveUtils: responsiveUtils,
|
||||||
|
onTapActionCallback: threadDetailCanPermanentlyDelete
|
||||||
|
? (_) => onThreadActionClick?.call(EmailActionType.deletePermanently)
|
||||||
|
: (_) => onThreadActionClick?.call(EmailActionType.moveToTrash),
|
||||||
|
),
|
||||||
|
],
|
||||||
if (!responsiveUtils.isMobile(context)) const Spacer(),
|
if (!responsiveUtils.isMobile(context)) const Spacer(),
|
||||||
if (AppUtils.getCurrentDirection(context) == TextDirection.rtl)
|
if (AppUtils.getCurrentDirection(context) == TextDirection.rtl)
|
||||||
...optionWidgets.reversed
|
...optionWidgets.reversed
|
||||||
|
|||||||
@@ -1154,12 +1154,6 @@
|
|||||||
"placeholders_order": [],
|
"placeholders_order": [],
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
"move_message": "Move message",
|
|
||||||
"@move_message": {
|
|
||||||
"type": "text",
|
|
||||||
"placeholders_order": [],
|
|
||||||
"placeholders": {}
|
|
||||||
},
|
|
||||||
"forwarded_message": "Forwarded message",
|
"forwarded_message": "Forwarded message",
|
||||||
"@forwarded_message": {
|
"@forwarded_message": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
|||||||
@@ -1167,13 +1167,6 @@ class AppLocalizations {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String get move_message {
|
|
||||||
return Intl.message(
|
|
||||||
'Move message',
|
|
||||||
name: 'move_message',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String get forwarded_message {
|
String get forwarded_message {
|
||||||
return Intl.message(
|
return Intl.message(
|
||||||
'Forwarded message',
|
'Forwarded message',
|
||||||
|
|||||||
Reference in New Issue
Block a user