TF-3941 Thread Detail Thread level mark read/star, archive and spam
This commit is contained in:
@@ -0,0 +1,23 @@
|
|||||||
|
# 68. Thread level actions
|
||||||
|
|
||||||
|
Date: 2025-08-18
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
- When user send an email, a copy of that email is created in Sent mailbox
|
||||||
|
- If user sent an email, and recipients contains own user, there will be 2 emails in the thread, one in Sent mailbox, and another in Inbox mailbox (or the other mailbox if there is email rule for it).
|
||||||
|
- This cause the thread has 2 emails with the exact same content.
|
||||||
|
- Current solution is filtering out the email in Sent mailbox when displaying thread.
|
||||||
|
- However, the thread level actions are ignoring these filtered-out emails.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
- All thread level actions will be applied to all emails of thread.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- If user move emails of thread to other mailbox, there will be email duplication due to the filtering conditions are not met anymore.
|
||||||
@@ -46,6 +46,7 @@ extension EmailActionTypeExtension on EmailActionType {
|
|||||||
switch(this) {
|
switch(this) {
|
||||||
case EmailActionType.archiveMessage:
|
case EmailActionType.archiveMessage:
|
||||||
case EmailActionType.moveToMailbox:
|
case EmailActionType.moveToMailbox:
|
||||||
|
case EmailActionType.archiveMessage:
|
||||||
return AppLocalizations.of(context).movedToFolder(destinationPath ?? '');
|
return AppLocalizations.of(context).movedToFolder(destinationPath ?? '');
|
||||||
case EmailActionType.moveToTrash:
|
case EmailActionType.moveToTrash:
|
||||||
return AppLocalizations.of(context).moved_to_trash;
|
return AppLocalizations.of(context).moved_to_trash;
|
||||||
|
|||||||
@@ -207,9 +207,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
: _threadDetailController?.emailIdsPresentation[_currentEmailId];
|
: _threadDetailController?.emailIdsPresentation[_currentEmailId];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isOnlyEmailInThread =>
|
|
||||||
_threadDetailController?.emailIdsPresentation.length == 1;
|
|
||||||
|
|
||||||
bool get calendarEventProcessing => viewState.value.fold(
|
bool get calendarEventProcessing => viewState.value.fold(
|
||||||
(failure) => false,
|
(failure) => false,
|
||||||
(success) => success is CalendarEventReplying);
|
(success) => success is CalendarEventReplying);
|
||||||
|
|||||||
@@ -252,40 +252,37 @@ class EmailView extends GetWidget<SingleEmailController> {
|
|||||||
onEmailActionClick: (presentationEmail, actionType) => controller.handleEmailAction(context, presentationEmail, actionType),
|
onEmailActionClick: (presentationEmail, actionType) => controller.handleEmailAction(context, presentationEmail, actionType),
|
||||||
isInsideThreadDetailView: isInsideThreadDetailView,
|
isInsideThreadDetailView: isInsideThreadDetailView,
|
||||||
emailLoaded: controller.currentEmailLoaded.value,
|
emailLoaded: controller.currentEmailLoaded.value,
|
||||||
onMoreActionClick: controller.isOnlyEmailInThread
|
onMoreActionClick: (presentationEmail, position) => controller.emailActionReactor.handleMoreEmailAction(
|
||||||
? null
|
mailboxContain: controller.getMailboxContain(presentationEmail),
|
||||||
: (presentationEmail, position) => controller.emailActionReactor.handleMoreEmailAction(
|
presentationEmail: presentationEmail,
|
||||||
mailboxContain: controller.getMailboxContain(presentationEmail),
|
position: position,
|
||||||
presentationEmail: presentationEmail,
|
responsiveUtils: controller.responsiveUtils,
|
||||||
position: position,
|
imagePaths: controller.imagePaths,
|
||||||
responsiveUtils: controller.responsiveUtils,
|
username: controller.session?.username,
|
||||||
imagePaths: controller.imagePaths,
|
handleEmailAction: (email, action) => controller.handleEmailAction(context, email, action),
|
||||||
username: controller.session?.username,
|
additionalActions: [
|
||||||
handleEmailAction: (email, action) => controller.handleEmailAction(context, email, action),
|
EmailActionType.forward,
|
||||||
additionalActions: [
|
EmailActionType.replyAll,
|
||||||
EmailActionType.forward,
|
EmailActionType.replyToList,
|
||||||
EmailActionType.replyAll,
|
EmailActionType.printAll,
|
||||||
EmailActionType.replyToList,
|
if (controller.responsiveUtils.isMobile(context))
|
||||||
EmailActionType.printAll,
|
EmailActionType.moveToMailbox,
|
||||||
if (controller.responsiveUtils.isMobile(context))
|
if (!controller.responsiveUtils.isDesktop(context)) ...[
|
||||||
EmailActionType.moveToMailbox,
|
EmailActionType.markAsStarred,
|
||||||
if (!controller.responsiveUtils.isDesktop(context)) ...[
|
EmailActionType.unMarkAsStarred,
|
||||||
EmailActionType.markAsStarred,
|
EmailActionType.moveToTrash,
|
||||||
EmailActionType.unMarkAsStarred,
|
EmailActionType.deletePermanently,
|
||||||
EmailActionType.moveToTrash,
|
],
|
||||||
EmailActionType.deletePermanently,
|
],
|
||||||
],
|
emailIsRead: presentationEmail.hasRead,
|
||||||
],
|
openBottomSheetContextMenu: controller.mailboxDashBoardController.openBottomSheetContextMenu,
|
||||||
emailIsRead: presentationEmail.hasRead,
|
openPopupMenu: controller.mailboxDashBoardController.openPopupMenu,
|
||||||
openBottomSheetContextMenu: controller.mailboxDashBoardController.openBottomSheetContextMenu,
|
),
|
||||||
openPopupMenu: controller.mailboxDashBoardController.openPopupMenu,
|
|
||||||
),
|
|
||||||
onToggleThreadDetailCollapseExpand: onToggleThreadDetailCollapseExpand,
|
onToggleThreadDetailCollapseExpand: onToggleThreadDetailCollapseExpand,
|
||||||
onTapAvatarActionClick: onToggleThreadDetailCollapseExpand,
|
onTapAvatarActionClick: onToggleThreadDetailCollapseExpand,
|
||||||
mailboxContain: presentationEmail.findMailboxContain(
|
mailboxContain: presentationEmail.findMailboxContain(
|
||||||
controller.mailboxDashBoardController.mapMailboxById,
|
controller.mailboxDashBoardController.mapMailboxById,
|
||||||
),
|
),
|
||||||
isOnlyEmailInThread: controller.isOnlyEmailInThread,
|
|
||||||
)),
|
)),
|
||||||
if (!controller.responsiveUtils.isMobile(context))
|
if (!controller.responsiveUtils.isMobile(context))
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
|||||||
@@ -23,6 +23,16 @@ extension EmailExtension on Email {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool checkEmailValidForThreadDetail(
|
||||||
|
MailboxId sentMailboxId,
|
||||||
|
String ownEmailAddress) {
|
||||||
|
return id != null && (
|
||||||
|
!inSentMailbox(sentMailboxId)
|
||||||
|
|| !fromMe(ownEmailAddress)
|
||||||
|
|| !recipientsHasMe(ownEmailAddress)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
bool inSentMailbox(MailboxId sentMailboxId) {
|
bool inSentMailbox(MailboxId sentMailboxId) {
|
||||||
return mailboxIds?[sentMailboxId] == true;
|
return mailboxIds?[sentMailboxId] == true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ class EmailViewAppBarWidget extends StatelessWidget {
|
|||||||
final EdgeInsetsGeometry? iconPadding;
|
final EdgeInsetsGeometry? iconPadding;
|
||||||
final EdgeInsetsGeometry? iconMargin;
|
final EdgeInsetsGeometry? iconMargin;
|
||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
final bool isOnlyEmailInThread;
|
|
||||||
|
|
||||||
EmailViewAppBarWidget({
|
EmailViewAppBarWidget({
|
||||||
Key? key,
|
Key? key,
|
||||||
@@ -53,7 +52,6 @@ class EmailViewAppBarWidget extends StatelessWidget {
|
|||||||
this.iconPadding,
|
this.iconPadding,
|
||||||
this.iconMargin,
|
this.iconMargin,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.isOnlyEmailInThread = false,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -266,8 +264,7 @@ class EmailViewAppBarWidget extends StatelessWidget {
|
|||||||
_getMarkStarButton(appLocalizations),
|
_getMarkStarButton(appLocalizations),
|
||||||
_getDeleteButton(appLocalizations),
|
_getDeleteButton(appLocalizations),
|
||||||
],
|
],
|
||||||
if (!isOnlyEmailInThread)
|
_getMoreButton(appLocalizations, isScreenWithShortestSide),
|
||||||
_getMoreButton(appLocalizations, isScreenWithShortestSide),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +44,6 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
|
|||||||
final OnTapAvatarActionClick? onTapAvatarActionClick;
|
final OnTapAvatarActionClick? onTapAvatarActionClick;
|
||||||
final PresentationMailbox? mailboxContain;
|
final PresentationMailbox? mailboxContain;
|
||||||
final bool showUnreadVisualization;
|
final bool showUnreadVisualization;
|
||||||
final bool isOnlyEmailInThread;
|
|
||||||
|
|
||||||
const InformationSenderAndReceiverBuilder({
|
const InformationSenderAndReceiverBuilder({
|
||||||
Key? key,
|
Key? key,
|
||||||
@@ -64,7 +63,6 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
|
|||||||
this.onTapAvatarActionClick,
|
this.onTapAvatarActionClick,
|
||||||
this.mailboxContain,
|
this.mailboxContain,
|
||||||
this.showUnreadVisualization = false,
|
this.showUnreadVisualization = false,
|
||||||
this.isOnlyEmailInThread = false,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -201,7 +199,6 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
|
|||||||
iconPadding: const EdgeInsets.all(8),
|
iconPadding: const EdgeInsets.all(8),
|
||||||
iconMargin: EdgeInsets.zero,
|
iconMargin: EdgeInsets.zero,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
isOnlyEmailInThread: isOnlyEmailInThread,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
+50
@@ -22,6 +22,7 @@ import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
|||||||
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
import 'package:jmap_dart_client/jmap/identities/identity.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart';
|
import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/model.dart';
|
||||||
@@ -181,6 +182,7 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_e
|
|||||||
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/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_mailbox_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart';
|
import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/action/thread_detail_ui_action.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/action/thread_detail_ui_action.dart';
|
||||||
import 'package:tmail_ui_user/main/deep_links/deep_link_data.dart';
|
import 'package:tmail_ui_user/main/deep_links/deep_link_data.dart';
|
||||||
import 'package:tmail_ui_user/main/deep_links/deep_links_manager.dart';
|
import 'package:tmail_ui_user/main/deep_links/deep_links_manager.dart';
|
||||||
@@ -1563,6 +1565,54 @@ class MailboxDashBoardController extends ReloadableController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void moveMultipleEmailInThreadDetail(
|
||||||
|
List<EmailInThreadDetailInfo> emailsInThreadDetailInfo, {
|
||||||
|
required MailboxId destinationMailboxId,
|
||||||
|
required EmailActionType emailActionType,
|
||||||
|
}) {
|
||||||
|
if (sessionCurrent == null || accountId.value == null) {
|
||||||
|
consumeState(Stream.value(
|
||||||
|
Left(MoveMultipleEmailToMailboxFailure(
|
||||||
|
emailActionType,
|
||||||
|
MoveAction.moving,
|
||||||
|
NotFoundSessionException()
|
||||||
|
))
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final currentMailboxes = <MailboxId, List<EmailId>>{};
|
||||||
|
for (final email in emailsInThreadDetailInfo) {
|
||||||
|
final mailboxIdContain = email.mailboxIdContain;
|
||||||
|
if (mailboxIdContain == null) continue;
|
||||||
|
|
||||||
|
currentMailboxes.putIfAbsent(mailboxIdContain, () => []).add(email.emailId);
|
||||||
|
}
|
||||||
|
final moveRequest = MoveToMailboxRequest(
|
||||||
|
currentMailboxes,
|
||||||
|
destinationMailboxId,
|
||||||
|
MoveAction.moving,
|
||||||
|
emailActionType,
|
||||||
|
destinationPath: currentContext == null
|
||||||
|
? mapMailboxById[destinationMailboxId]?.name?.name
|
||||||
|
: mapMailboxById[destinationMailboxId]?.getDisplayName(currentContext!),
|
||||||
|
);
|
||||||
|
final emailIdsWithReadStatus = Map.fromEntries(
|
||||||
|
emailsInThreadDetailInfo.map(
|
||||||
|
(email) => MapEntry(
|
||||||
|
email.emailId,
|
||||||
|
email.keywords?[KeyWordIdentifier.emailSeen] == true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
_moveSelectedEmailMultipleToMailboxAction(
|
||||||
|
sessionCurrent!,
|
||||||
|
accountId.value!,
|
||||||
|
moveRequest,
|
||||||
|
emailIdsWithReadStatus,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void deleteSelectionEmailsPermanently(
|
void deleteSelectionEmailsPermanently(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
DeleteActionType actionType,
|
DeleteActionType actionType,
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ import 'package:jmap_dart_client/jmap/account_id.dart';
|
|||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
import 'package:model/extensions/account_id_extensions.dart';
|
import 'package:model/extensions/account_id_extensions.dart';
|
||||||
import 'package:model/extensions/email_id_extensions.dart';
|
import 'package:model/extensions/email_id_extensions.dart';
|
||||||
import 'package:tmail_ui_user/features/caching/utils/cache_utils.dart';
|
import 'package:tmail_ui_user/features/caching/utils/cache_utils.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/presentation/extensions/email_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/data/extensions/email_extension.dart';
|
import 'package:tmail_ui_user/features/thread/data/extensions/email_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/data/model/email_cache.dart';
|
import 'package:tmail_ui_user/features/thread/data/model/email_cache.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
|
||||||
|
|
||||||
extension ListEmailExtension on List<Email> {
|
extension ListEmailExtension on List<Email> {
|
||||||
Map<String, EmailCache> toMapCache(AccountId accountId, UserName userName) {
|
Map<String, EmailCache> toMapCache(AccountId accountId, UserName userName) {
|
||||||
@@ -39,4 +42,23 @@ extension ListEmailExtension on List<Email> {
|
|||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<EmailInThreadDetailInfo> toEmailsInThreadDetailInfo({
|
||||||
|
required MailboxId? sentMailboxId,
|
||||||
|
required String? ownEmailAddress,
|
||||||
|
}) {
|
||||||
|
return map(
|
||||||
|
(email) => EmailInThreadDetailInfo(
|
||||||
|
emailId: email.id!,
|
||||||
|
keywords: email.keywords,
|
||||||
|
mailboxIds: email.mailboxIds,
|
||||||
|
isValidToDisplay: sentMailboxId == null || ownEmailAddress == null
|
||||||
|
? true
|
||||||
|
: email.checkEmailValidForThreadDetail(
|
||||||
|
sentMailboxId,
|
||||||
|
ownEmailAddress,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,9 @@ import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
|||||||
import 'package:model/email/presentation_email.dart';
|
import 'package:model/email/presentation_email.dart';
|
||||||
import 'package:model/extensions/presentation_email_extension.dart';
|
import 'package:model/extensions/presentation_email_extension.dart';
|
||||||
import 'package:model/mailbox/presentation_mailbox.dart';
|
import 'package:model/mailbox/presentation_mailbox.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/presentation/extensions/email_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
|
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/navigation_router.dart';
|
import 'package:tmail_ui_user/main/routes/navigation_router.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_utils.dart';
|
import 'package:tmail_ui_user/main/routes/route_utils.dart';
|
||||||
@@ -56,4 +58,23 @@ extension ListPresentationEmailExtensions on List<PresentationEmail> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<EmailInThreadDetailInfo> toEmailsInThreadDetailInfo({
|
||||||
|
required MailboxId? sentMailboxId,
|
||||||
|
required String? ownEmailAddress,
|
||||||
|
}) {
|
||||||
|
return map(
|
||||||
|
(email) => EmailInThreadDetailInfo(
|
||||||
|
emailId: email.id!,
|
||||||
|
keywords: email.keywords,
|
||||||
|
mailboxIds: email.mailboxIds,
|
||||||
|
isValidToDisplay: sentMailboxId == null || ownEmailAddress == null
|
||||||
|
? true
|
||||||
|
: email.toEmail().checkEmailValidForThreadDetail(
|
||||||
|
sentMailboxId,
|
||||||
|
ownEmailAddress,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@ import 'package:model/extensions/list_email_extension.dart';
|
|||||||
import 'package:tmail_ui_user/features/email/presentation/extensions/email_extension.dart';
|
import 'package:tmail_ui_user/features/email/presentation/extensions/email_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/data/data_source/thread_detail_data_source.dart';
|
import 'package:tmail_ui_user/features/thread_detail/data/data_source/thread_detail_data_source.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/exceptions/empty_thread_detail_exception.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/exceptions/empty_thread_detail_exception.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/repository/thread_detail_repository.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/repository/thread_detail_repository.dart';
|
||||||
|
|
||||||
class ThreadDetailRepositoryImpl implements ThreadDetailRepository {
|
class ThreadDetailRepositoryImpl implements ThreadDetailRepository {
|
||||||
@@ -20,14 +21,13 @@ class ThreadDetailRepositoryImpl implements ThreadDetailRepository {
|
|||||||
final Map<DataSourceType, ThreadDetailDataSource> threadDetailDataSource;
|
final Map<DataSourceType, ThreadDetailDataSource> threadDetailDataSource;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<EmailId>> getThreadById(
|
Future<List<EmailInThreadDetailInfo>> getThreadById(
|
||||||
ThreadId threadId,
|
ThreadId threadId,
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
MailboxId sentMailboxId,
|
MailboxId sentMailboxId,
|
||||||
String ownEmailAddress, {
|
String ownEmailAddress,
|
||||||
required EmailId? selectedEmailId,
|
) async {
|
||||||
}) async {
|
|
||||||
final originalEmailIds = await threadDetailDataSource[DataSourceType.network]!
|
final originalEmailIds = await threadDetailDataSource[DataSourceType.network]!
|
||||||
.getThreadById(threadId, accountId);
|
.getThreadById(threadId, accountId);
|
||||||
|
|
||||||
@@ -44,27 +44,24 @@ class ThreadDetailRepositoryImpl implements ThreadDetailRepository {
|
|||||||
emailIds,
|
emailIds,
|
||||||
sentMailboxId,
|
sentMailboxId,
|
||||||
ownEmailAddress,
|
ownEmailAddress,
|
||||||
selectedEmailId: selectedEmailId,
|
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
return filteredEmailIds
|
return filteredEmailIds
|
||||||
.reduce((prev, curr) => prev + curr)
|
.reduce((prev, curr) => prev..addAll(curr))
|
||||||
.sortWithResult(EmailComparator(
|
.sortWithResult(EmailComparator(
|
||||||
EmailComparatorProperty.receivedAt
|
EmailComparatorProperty.receivedAt
|
||||||
)..setIsAscending(true))
|
)..setIsAscending(true))
|
||||||
.map((e) => e.id!)
|
.toEmailsInThreadDetailInfo();
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Email>> _filterBadEmails(
|
Future<Map<Email, bool>> _filterBadEmails(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
MailboxId sentMailboxId,
|
MailboxId sentMailboxId,
|
||||||
String ownEmailAddress, {
|
String ownEmailAddress,
|
||||||
required EmailId? selectedEmailId,
|
) async {
|
||||||
}) async {
|
|
||||||
int retry = 3;
|
int retry = 3;
|
||||||
while (retry > 0) {
|
while (retry > 0) {
|
||||||
try {
|
try {
|
||||||
@@ -81,37 +78,23 @@ class ThreadDetailRepositoryImpl implements ThreadDetailRepository {
|
|||||||
EmailProperty.cc,
|
EmailProperty.cc,
|
||||||
EmailProperty.bcc,
|
EmailProperty.bcc,
|
||||||
EmailProperty.receivedAt,
|
EmailProperty.receivedAt,
|
||||||
|
EmailProperty.keywords,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
return emails
|
return {
|
||||||
.where((email) => checkEmailValidForThreadDetail(
|
for (final email in emails)
|
||||||
email,
|
email: email.checkEmailValidForThreadDetail(
|
||||||
sentMailboxId,
|
sentMailboxId,
|
||||||
ownEmailAddress,
|
ownEmailAddress,
|
||||||
selectedEmailId: selectedEmailId,
|
),
|
||||||
))
|
};
|
||||||
.toList();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
retry--;
|
retry--;
|
||||||
|
|
||||||
if (retry <= 0) rethrow;
|
if (retry <= 0) rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [];
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
bool checkEmailValidForThreadDetail(
|
|
||||||
Email email,
|
|
||||||
MailboxId sentMailboxId,
|
|
||||||
String ownEmailAddress, {
|
|
||||||
required EmailId? selectedEmailId
|
|
||||||
}) {
|
|
||||||
return email.id != null && (
|
|
||||||
!email.inSentMailbox(sentMailboxId)
|
|
||||||
|| !email.fromMe(ownEmailAddress)
|
|
||||||
|| !email.recipientsHasMe(ownEmailAddress)
|
|
||||||
|| email.id == selectedEmailId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -130,4 +113,28 @@ class ThreadDetailRepositoryImpl implements ThreadDetailRepository {
|
|||||||
return threadDetailDataSource[DataSourceType.local]!
|
return threadDetailDataSource[DataSourceType.local]!
|
||||||
.getThreadDetailStatus();
|
.getThreadDetailStatus();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SortListEmailInThreadDetailInfo on Map<Email, bool> {
|
||||||
|
Map<Email, bool> sortWithResult(
|
||||||
|
EmailComparator comparator,
|
||||||
|
) {
|
||||||
|
final originalEmails = keys.toList();
|
||||||
|
final sortedEmails = originalEmails.sortWithResult(comparator);
|
||||||
|
return {
|
||||||
|
for (final email in sortedEmails)
|
||||||
|
email: this[email] ?? false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
List<EmailInThreadDetailInfo> toEmailsInThreadDetailInfo() {
|
||||||
|
return entries.map(
|
||||||
|
(entry) => EmailInThreadDetailInfo(
|
||||||
|
emailId: entry.key.id!,
|
||||||
|
keywords: entry.key.keywords,
|
||||||
|
mailboxIds: entry.key.mailboxIds,
|
||||||
|
isValidToDisplay: entry.value,
|
||||||
|
),
|
||||||
|
).toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
|
||||||
|
|
||||||
|
extension ListEmailInThreadDetailInfoExtension on List<EmailInThreadDetailInfo> {
|
||||||
|
List<EmailId> get emailIdsToDisplay =>
|
||||||
|
where((email) => email.isValidToDisplay)
|
||||||
|
.map((email) => email.emailId)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
|
|
||||||
|
class EmailInThreadDetailInfo with EquatableMixin {
|
||||||
|
const EmailInThreadDetailInfo({
|
||||||
|
required this.emailId,
|
||||||
|
required this.keywords,
|
||||||
|
required this.mailboxIds,
|
||||||
|
required this.isValidToDisplay,
|
||||||
|
});
|
||||||
|
|
||||||
|
final EmailId emailId;
|
||||||
|
final Map<KeyWordIdentifier, bool>? keywords;
|
||||||
|
final Map<MailboxId, bool>? mailboxIds;
|
||||||
|
final bool isValidToDisplay;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [emailId, keywords, mailboxIds, isValidToDisplay];
|
||||||
|
|
||||||
|
EmailInThreadDetailInfo copyWith({
|
||||||
|
EmailId? emailId,
|
||||||
|
Map<KeyWordIdentifier, bool>? keywords,
|
||||||
|
Map<MailboxId, bool>? mailboxIds,
|
||||||
|
bool? isValidToDisplay,
|
||||||
|
}) {
|
||||||
|
return EmailInThreadDetailInfo(
|
||||||
|
emailId: emailId ?? this.emailId,
|
||||||
|
keywords: keywords ?? this.keywords,
|
||||||
|
mailboxIds: mailboxIds ?? this.mailboxIds,
|
||||||
|
isValidToDisplay: isValidToDisplay ?? this.isValidToDisplay,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
MailboxId? get mailboxIdContain {
|
||||||
|
return mailboxIds?.entries
|
||||||
|
.firstWhereOrNull((entry) => entry.value)
|
||||||
|
?.key;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,16 +3,16 @@ import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
|
|||||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
|
||||||
|
|
||||||
abstract class ThreadDetailRepository {
|
abstract class ThreadDetailRepository {
|
||||||
Future<List<EmailId>> getThreadById(
|
Future<List<EmailInThreadDetailInfo>> getThreadById(
|
||||||
ThreadId threadId,
|
ThreadId threadId,
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
MailboxId sentMailboxId,
|
MailboxId sentMailboxId,
|
||||||
String ownEmailAddress, {
|
String ownEmailAddress,
|
||||||
required EmailId? selectedEmailId,
|
);
|
||||||
});
|
|
||||||
|
|
||||||
Future<List<Email>> getEmailsByIds(
|
Future<List<Email>> getEmailsByIds(
|
||||||
Session session,
|
Session session,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:core/presentation/state/failure.dart';
|
import 'package:core/presentation/state/failure.dart';
|
||||||
import 'package:core/presentation/state/success.dart';
|
import 'package:core/presentation/state/success.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
|
||||||
|
|
||||||
class GettingThreadById extends LoadingState {
|
class GettingThreadById extends LoadingState {
|
||||||
final bool updateCurrentThreadDetail;
|
final bool updateCurrentThreadDetail;
|
||||||
@@ -16,25 +17,32 @@ class GetThreadByIdSuccess extends UIState {
|
|||||||
final ThreadId? threadId;
|
final ThreadId? threadId;
|
||||||
final bool updateCurrentThreadDetail;
|
final bool updateCurrentThreadDetail;
|
||||||
final bool skipLoadingMetadata;
|
final bool skipLoadingMetadata;
|
||||||
|
final List<EmailInThreadDetailInfo> emailsInThreadDetailInfo;
|
||||||
|
|
||||||
GetThreadByIdSuccess(
|
GetThreadByIdSuccess(
|
||||||
this.emailIds, {
|
this.emailIds, {
|
||||||
required this.threadId,
|
required this.threadId,
|
||||||
this.updateCurrentThreadDetail = false,
|
this.updateCurrentThreadDetail = false,
|
||||||
this.skipLoadingMetadata = false,
|
this.skipLoadingMetadata = false,
|
||||||
|
this.emailsInThreadDetailInfo = const [],
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
emailIds,
|
emailIds,
|
||||||
threadId,
|
threadId,
|
||||||
|
emailsInThreadDetailInfo,
|
||||||
updateCurrentThreadDetail,
|
updateCurrentThreadDetail,
|
||||||
skipLoadingMetadata,
|
skipLoadingMetadata,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
class PreloadEmailIdsInThreadSuccess extends GetThreadByIdSuccess {
|
class PreloadEmailIdsInThreadSuccess extends GetThreadByIdSuccess {
|
||||||
PreloadEmailIdsInThreadSuccess(super.emailIds, {required super.threadId});
|
PreloadEmailIdsInThreadSuccess(
|
||||||
|
super.emailIds, {
|
||||||
|
required super.threadId,
|
||||||
|
super.emailsInThreadDetailInfo,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class GetThreadByIdFailure extends FeatureFailure {
|
class GetThreadByIdFailure extends FeatureFailure {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ 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/core/session/session.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/domain/extensions/list_email_in_thread_detail_info_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/repository/thread_detail_repository.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/repository/thread_detail_repository.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
||||||
|
|
||||||
@@ -20,7 +21,6 @@ class GetThreadByIdInteractor {
|
|||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
MailboxId sentMailboxId,
|
MailboxId sentMailboxId,
|
||||||
String ownEmailAddress, {
|
String ownEmailAddress, {
|
||||||
required EmailId? selectedEmailId,
|
|
||||||
bool updateCurrentThreadDetail = false,
|
bool updateCurrentThreadDetail = false,
|
||||||
}) async* {
|
}) async* {
|
||||||
try {
|
try {
|
||||||
@@ -33,13 +33,13 @@ class GetThreadByIdInteractor {
|
|||||||
accountId,
|
accountId,
|
||||||
sentMailboxId,
|
sentMailboxId,
|
||||||
ownEmailAddress,
|
ownEmailAddress,
|
||||||
selectedEmailId: selectedEmailId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
yield Right(GetThreadByIdSuccess(
|
yield Right(GetThreadByIdSuccess(
|
||||||
result,
|
result.emailIdsToDisplay,
|
||||||
threadId: threadId,
|
threadId: threadId,
|
||||||
updateCurrentThreadDetail: updateCurrentThreadDetail,
|
updateCurrentThreadDetail: updateCurrentThreadDetail,
|
||||||
|
emailsInThreadDetailInfo: result,
|
||||||
));
|
));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError('GetEmailIdsByThreadIdInteractor::execute(): Exception: $e');
|
logError('GetEmailIdsByThreadIdInteractor::execute(): Exception: $e');
|
||||||
@@ -51,7 +51,6 @@ class GetThreadByIdInteractor {
|
|||||||
accountId,
|
accountId,
|
||||||
sentMailboxId,
|
sentMailboxId,
|
||||||
ownEmailAddress,
|
ownEmailAddress,
|
||||||
selectedEmailId: selectedEmailId,
|
|
||||||
),
|
),
|
||||||
updateCurrentThreadDetail: updateCurrentThreadDetail,
|
updateCurrentThreadDetail: updateCurrentThreadDetail,
|
||||||
));
|
));
|
||||||
|
|||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
|
import 'package:model/mailbox/presentation_mailbox.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
|
||||||
|
|
||||||
|
extension GetThreadDetailActionStatus on ThreadDetailController {
|
||||||
|
bool get threadDetailIsStarred {
|
||||||
|
return emailsInThreadDetailInfo.every(
|
||||||
|
(email) {
|
||||||
|
return email.keywords?.containsKey(KeyWordIdentifier.emailFlagged) ==
|
||||||
|
true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get threadDetailIsRead {
|
||||||
|
return emailsInThreadDetailInfo.every(
|
||||||
|
(email) {
|
||||||
|
return email.keywords?.containsKey(KeyWordIdentifier.emailSeen) == true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
MailboxId? getMailboxIdByRole(Role role) {
|
||||||
|
return mailboxDashBoardController.mapDefaultMailboxIdByRole[role];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get threadDetailIsArchived {
|
||||||
|
final archiveMailboxId = getMailboxIdByRole(
|
||||||
|
PresentationMailbox.roleArchive,
|
||||||
|
);
|
||||||
|
return emailsInThreadDetailInfo.every(
|
||||||
|
(email) {
|
||||||
|
return email.mailboxIds?[archiveMailboxId] == true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get threadDetailIsSpam {
|
||||||
|
final spamMailboxId =
|
||||||
|
getMailboxIdByRole(PresentationMailbox.roleJunk) ??
|
||||||
|
getMailboxIdByRole(PresentationMailbox.roleSpam);
|
||||||
|
return emailsInThreadDetailInfo.every(
|
||||||
|
(email) {
|
||||||
|
return email.mailboxIds?[spamMailboxId] == true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:model/email/email_action_type.dart';
|
|
||||||
import 'package:model/extensions/presentation_email_extension.dart';
|
import 'package:model/extensions/presentation_email_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/email_view.dart';
|
import 'package:tmail_ui_user/features/email/presentation/email_view.dart';
|
||||||
import 'package:model/email/email_in_thread_status.dart';
|
import 'package:model/email/email_in_thread_status.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_open_context_menu_extension.dart';
|
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/get_thread_detail_email_mailbox_contains.dart';
|
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/load_more_thread_detail_emails.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/load_more_thread_detail_emails.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/thread_detail_on_email_action_click.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/thread_detail_on_email_action_click.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/thread_detail_open_email_address_detail_action.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/thread_detail_open_email_address_detail_action.dart';
|
||||||
@@ -14,7 +11,6 @@ import 'package:tmail_ui_user/features/thread_detail/presentation/extension/togg
|
|||||||
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/features/thread_detail/presentation/widgets/thread_detail_collapsed_email.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/widgets/thread_detail_collapsed_email.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/widgets/thread_detail_load_more_circle.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/widgets/thread_detail_load_more_circle.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
|
||||||
|
|
||||||
extension GetThreadDetailEmailViews on ThreadDetailController {
|
extension GetThreadDetailEmailViews on ThreadDetailController {
|
||||||
List<Widget> getThreadDetailEmailViews() {
|
List<Widget> getThreadDetailEmailViews() {
|
||||||
@@ -65,33 +61,6 @@ extension GetThreadDetailEmailViews on ThreadDetailController {
|
|||||||
),
|
),
|
||||||
emailLoaded: null,
|
emailLoaded: null,
|
||||||
onEmailActionClick: threadDetailOnEmailActionClick,
|
onEmailActionClick: threadDetailOnEmailActionClick,
|
||||||
onMoreActionClick: (presentationEmail, position) => emailActionReactor.handleMoreEmailAction(
|
|
||||||
mailboxContain: getThreadDetailEmailMailboxContains(presentationEmail),
|
|
||||||
presentationEmail: presentationEmail,
|
|
||||||
position: position,
|
|
||||||
responsiveUtils: responsiveUtils,
|
|
||||||
imagePaths: imagePaths,
|
|
||||||
username: session?.username,
|
|
||||||
handleEmailAction: threadDetailOnEmailActionClick,
|
|
||||||
additionalActions: [
|
|
||||||
EmailActionType.forward,
|
|
||||||
EmailActionType.replyAll,
|
|
||||||
EmailActionType.replyToList,
|
|
||||||
EmailActionType.printAll,
|
|
||||||
if (currentContext != null &&
|
|
||||||
responsiveUtils.isMobile(currentContext!))
|
|
||||||
EmailActionType.moveToMailbox,
|
|
||||||
if (!responsiveUtils.isDesktop(currentContext!)) ...[
|
|
||||||
EmailActionType.markAsStarred,
|
|
||||||
EmailActionType.unMarkAsStarred,
|
|
||||||
EmailActionType.moveToTrash,
|
|
||||||
EmailActionType.deletePermanently,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
emailIsRead: presentationEmail.hasRead,
|
|
||||||
openBottomSheetContextMenu: mailboxDashBoardController.openBottomSheetContextMenu,
|
|
||||||
openPopupMenu: mailboxDashBoardController.openPopupMenu,
|
|
||||||
),
|
|
||||||
openEmailAddressDetailAction: (_, emailAddress) {
|
openEmailAddressDetailAction: (_, emailAddress) {
|
||||||
openEmailAddressDetailAction(emailAddress);
|
openEmailAddressDetailAction(emailAddress);
|
||||||
},
|
},
|
||||||
|
|||||||
+11
@@ -11,15 +11,22 @@ extension HandleGetEmailIdsByThreadIdSuccess on ThreadDetailController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final allEmailIds = success.emailIds;
|
final allEmailIds = success.emailIds;
|
||||||
|
final allEmailsInThreadDetailInfo = success.emailsInThreadDetailInfo;
|
||||||
if (success.updateCurrentThreadDetail) {
|
if (success.updateCurrentThreadDetail) {
|
||||||
final newEmailIds = allEmailIds.where(
|
final newEmailIds = allEmailIds.where(
|
||||||
(emailId) => !emailIdsPresentation.keys.contains(emailId),
|
(emailId) => !emailIdsPresentation.keys.contains(emailId),
|
||||||
);
|
);
|
||||||
|
final newEmailsInThreadDetailInfo = allEmailsInThreadDetailInfo.where(
|
||||||
|
(email) => !emailsInThreadDetailInfo.contains(email),
|
||||||
|
);
|
||||||
emailIdsPresentation
|
emailIdsPresentation
|
||||||
..removeWhere((key, _) => !allEmailIds.contains(key))
|
..removeWhere((key, _) => !allEmailIds.contains(key))
|
||||||
..addEntries(
|
..addEntries(
|
||||||
newEmailIds.map((emailId) => MapEntry(emailId, null)),
|
newEmailIds.map((emailId) => MapEntry(emailId, null)),
|
||||||
);
|
);
|
||||||
|
emailsInThreadDetailInfo
|
||||||
|
..removeWhere((e) => !allEmailsInThreadDetailInfo.contains(e))
|
||||||
|
..addAll(newEmailsInThreadDetailInfo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,5 +38,9 @@ extension HandleGetEmailIdsByThreadIdSuccess on ThreadDetailController {
|
|||||||
? emailIdsPresentation[id] ?? selectedEmail
|
? emailIdsPresentation[id] ?? selectedEmail
|
||||||
: null,
|
: null,
|
||||||
};
|
};
|
||||||
|
if ((isThreadDetailEnabled && success is! PreloadEmailIdsInThreadSuccess) ||
|
||||||
|
(!isThreadDetailEnabled && success is PreloadEmailIdsInThreadSuccess)) {
|
||||||
|
emailsInThreadDetailInfo.value = allEmailsInThreadDetailInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
import 'package:model/email/read_actions.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/close_thread_detail_action.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/toggle_thread_detail_collape_expand.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
|
extension HandleMarkMultipleEmailsReadSuccess on ThreadDetailController {
|
||||||
|
void handleMarkMultipleEmailsReadSuccess(
|
||||||
|
UIState success,
|
||||||
|
ReadActions readActions,
|
||||||
|
List<EmailId> emailIds,
|
||||||
|
) {
|
||||||
|
mailboxDashBoardController.consumeState(Stream.value(Right(success)));
|
||||||
|
if (readActions == ReadActions.markAsRead) return;
|
||||||
|
|
||||||
|
if (emailsInThreadDetailInfo.length == 1) {
|
||||||
|
closeThreadDetailAction(currentContext);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final expandedEmailId = currentExpandedEmailId.value;
|
||||||
|
if (expandedEmailId == null) return;
|
||||||
|
|
||||||
|
final expandedEmail = emailIdsPresentation[expandedEmailId];
|
||||||
|
if (emailIds.contains(expandedEmailId) && expandedEmail != null) {
|
||||||
|
toggleThreadDetailCollapeExpand(expandedEmail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -7,6 +7,7 @@ import 'package:model/extensions/keyword_identifier_extension.dart';
|
|||||||
import 'package:model/extensions/list_email_extension.dart';
|
import 'package:model/extensions/list_email_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
|
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/extensions/email_extension.dart';
|
import 'package:tmail_ui_user/features/email/presentation/extensions/email_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/extensions/list_email_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
|
||||||
@@ -34,6 +35,10 @@ extension HandleRefreshThreadDetailAction on ThreadDetailController {
|
|||||||
[currentEmailId],
|
[currentEmailId],
|
||||||
updateCurrentThreadDetail: true,
|
updateCurrentThreadDetail: true,
|
||||||
threadId: mailboxDashBoardController.selectedEmail.value?.threadId,
|
threadId: mailboxDashBoardController.selectedEmail.value?.threadId,
|
||||||
|
emailsInThreadDetailInfo: action.emailChangeResponse.updated?.toEmailsInThreadDetailInfo(
|
||||||
|
sentMailboxId: sentMailboxId,
|
||||||
|
ownEmailAddress: ownEmailAddress,
|
||||||
|
) ?? [],
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +76,14 @@ extension HandleRefreshThreadDetailAction on ThreadDetailController {
|
|||||||
updated: emailsUpdated,
|
updated: emailsUpdated,
|
||||||
destroyed: emailIdsDestroyed.toList(),
|
destroyed: emailIdsDestroyed.toList(),
|
||||||
);
|
);
|
||||||
|
final afterRefreshedThreadDetailInfo = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
|
||||||
|
original: emailsInThreadDetailInfo,
|
||||||
|
created: emailsCreated,
|
||||||
|
updated: emailsUpdated,
|
||||||
|
destroyed: emailIdsDestroyed.toList(),
|
||||||
|
sentMailboxId: sentMailboxId,
|
||||||
|
ownEmailAddress: ownEmailAddress,
|
||||||
|
);
|
||||||
|
|
||||||
if (afterRefreshedEmailIds.isEmpty) {
|
if (afterRefreshedEmailIds.isEmpty) {
|
||||||
closeThreadDetailAction(currentContext);
|
closeThreadDetailAction(currentContext);
|
||||||
@@ -81,6 +94,7 @@ extension HandleRefreshThreadDetailAction on ThreadDetailController {
|
|||||||
afterRefreshedEmailIds,
|
afterRefreshedEmailIds,
|
||||||
threadId: currentThreadId,
|
threadId: currentThreadId,
|
||||||
updateCurrentThreadDetail: true,
|
updateCurrentThreadDetail: true,
|
||||||
|
emailsInThreadDetailInfo: afterRefreshedThreadDetailInfo,
|
||||||
))));
|
))));
|
||||||
consumeState(Stream.value(Right(GetEmailsByIdsSuccess(
|
consumeState(Stream.value(Right(GetEmailsByIdsSuccess(
|
||||||
afterRefreshedEmails,
|
afterRefreshedEmails,
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
|
import 'package:model/email/email_action_type.dart';
|
||||||
|
import 'package:model/email/mark_star_action.dart';
|
||||||
|
import 'package:model/email/read_actions.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/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/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/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_detail/presentation/extension/close_thread_detail_action.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/main/localizations/app_localizations.dart';
|
||||||
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
|
extension OnThreadDetailActionClick on ThreadDetailController {
|
||||||
|
void onThreadDetailActionClick(EmailActionType threadDetailActionType) {
|
||||||
|
switch (threadDetailActionType) {
|
||||||
|
case EmailActionType.markAsRead:
|
||||||
|
case EmailActionType.markAsUnread:
|
||||||
|
if (session == null || accountId == null) {
|
||||||
|
consumeState(Stream.value(Left(MarkAsMultipleEmailReadFailure(
|
||||||
|
threadDetailActionType == EmailActionType.markAsRead
|
||||||
|
? ReadActions.markAsRead
|
||||||
|
: ReadActions.markAsUnread,
|
||||||
|
NotFoundSessionException(),
|
||||||
|
))));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
consumeState(markAsMultipleEmailReadInteractor.execute(
|
||||||
|
session!,
|
||||||
|
accountId!,
|
||||||
|
emailsInThreadDetailInfo.map((e) => e.emailId).toList(),
|
||||||
|
threadDetailActionType == EmailActionType.markAsRead
|
||||||
|
? ReadActions.markAsRead
|
||||||
|
: ReadActions.markAsUnread,
|
||||||
|
{},
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case EmailActionType.markAsStarred:
|
||||||
|
case EmailActionType.unMarkAsStarred:
|
||||||
|
if (session == null || accountId == null) {
|
||||||
|
consumeState(Stream.value(Left(MarkAsStarMultipleEmailFailure(
|
||||||
|
threadDetailActionType == EmailActionType.markAsStarred
|
||||||
|
? MarkStarAction.markStar
|
||||||
|
: MarkStarAction.unMarkStar,
|
||||||
|
NotFoundSessionException(),
|
||||||
|
))));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
consumeState(markAsStarMultipleEmailInteractor.execute(
|
||||||
|
session!,
|
||||||
|
accountId!,
|
||||||
|
emailsInThreadDetailInfo.map((e) => e.emailId).toList(),
|
||||||
|
threadDetailActionType == EmailActionType.markAsStarred
|
||||||
|
? MarkStarAction.markStar
|
||||||
|
: MarkStarAction.unMarkStar,
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case EmailActionType.archiveMessage:
|
||||||
|
final mailboxId = mailboxDashBoardController.getMailboxIdByRole(
|
||||||
|
PresentationMailbox.roleArchive,
|
||||||
|
);
|
||||||
|
if (mailboxId == null) return;
|
||||||
|
|
||||||
|
_moveToMailbox(mailboxId, threadDetailActionType);
|
||||||
|
break;
|
||||||
|
case EmailActionType.moveToSpam:
|
||||||
|
final mailboxId = mailboxDashBoardController.getMailboxIdByRole(
|
||||||
|
PresentationMailbox.roleJunk,
|
||||||
|
) ?? mailboxDashBoardController.getMailboxIdByRole(
|
||||||
|
PresentationMailbox.roleSpam,
|
||||||
|
);
|
||||||
|
if (mailboxId == null) return;
|
||||||
|
|
||||||
|
_moveToMailbox(mailboxId, threadDetailActionType);
|
||||||
|
break;
|
||||||
|
case EmailActionType.unSpam:
|
||||||
|
final mailboxId = mailboxDashBoardController.getMailboxIdByRole(
|
||||||
|
PresentationMailbox.roleInbox,
|
||||||
|
);
|
||||||
|
if (mailboxId == null) return;
|
||||||
|
|
||||||
|
_moveToMailbox(mailboxId, threadDetailActionType);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onThreadDetailMoreActionClick(RelativeRect? position) {
|
||||||
|
if (currentContext == null) return;
|
||||||
|
|
||||||
|
final moreActions = [
|
||||||
|
threadDetailIsRead
|
||||||
|
? EmailActionType.markAsUnread
|
||||||
|
: EmailActionType.markAsRead,
|
||||||
|
threadDetailIsStarred
|
||||||
|
? EmailActionType.unMarkAsStarred
|
||||||
|
: EmailActionType.markAsStarred,
|
||||||
|
if (!threadDetailIsArchived) EmailActionType.archiveMessage,
|
||||||
|
threadDetailIsSpam ? EmailActionType.unSpam : EmailActionType.moveToSpam,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (position == null) {
|
||||||
|
mailboxDashBoardController.openBottomSheetContextMenu(
|
||||||
|
context: currentContext!,
|
||||||
|
itemActions: moreActions
|
||||||
|
.map(
|
||||||
|
(action) => ContextItemEmailAction(
|
||||||
|
action,
|
||||||
|
AppLocalizations.of(currentContext!),
|
||||||
|
imagePaths,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
onContextMenuActionClick: (action) {
|
||||||
|
popBack();
|
||||||
|
onThreadDetailActionClick(action.action);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
mailboxDashBoardController.openPopupMenu(
|
||||||
|
currentContext!,
|
||||||
|
position,
|
||||||
|
moreActions.map((action) {
|
||||||
|
return PopupMenuItem(
|
||||||
|
key: Key('${action.name}_action'),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: PopupMenuItemActionWidget(
|
||||||
|
menuAction: PopupMenuItemEmailAction(
|
||||||
|
action,
|
||||||
|
AppLocalizations.of(currentContext!),
|
||||||
|
imagePaths,
|
||||||
|
),
|
||||||
|
menuActionClick: (_) {
|
||||||
|
popBack();
|
||||||
|
onThreadDetailActionClick(action);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _moveToMailbox(MailboxId mailboxId, EmailActionType emailActionType) {
|
||||||
|
closeThreadDetailAction(currentContext);
|
||||||
|
mailboxDashBoardController.moveMultipleEmailInThreadDetail(
|
||||||
|
emailsInThreadDetailInfo,
|
||||||
|
destinationMailboxId: mailboxId,
|
||||||
|
emailActionType: emailActionType,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
-322
@@ -1,18 +1,10 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
|
|
||||||
import 'package:model/email/email_action_type.dart';
|
import 'package:model/email/email_action_type.dart';
|
||||||
import 'package:model/email/mark_star_action.dart';
|
|
||||||
import 'package:model/email/presentation_email.dart';
|
import 'package:model/email/presentation_email.dart';
|
||||||
import 'package:model/email/read_actions.dart';
|
import 'package:model/email/read_actions.dart';
|
||||||
import 'package:model/extensions/presentation_email_extension.dart';
|
|
||||||
import 'package:model/extensions/session_extension.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
|
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/close_thread_detail_action.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/routes/route_navigation.dart';
|
|
||||||
|
|
||||||
extension ThreadDetailOnEmailActionClick on ThreadDetailController {
|
extension ThreadDetailOnEmailActionClick on ThreadDetailController {
|
||||||
void threadDetailOnEmailActionClick(
|
void threadDetailOnEmailActionClick(
|
||||||
@@ -23,60 +15,9 @@ extension ThreadDetailOnEmailActionClick on ThreadDetailController {
|
|||||||
case EmailActionType.markAsRead:
|
case EmailActionType.markAsRead:
|
||||||
_markRead(presentationEmail);
|
_markRead(presentationEmail);
|
||||||
break;
|
break;
|
||||||
case EmailActionType.markAsUnread:
|
|
||||||
_unRead(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.markAsStarred:
|
|
||||||
_markStar(presentationEmail, MarkStarAction.markStar);
|
|
||||||
break;
|
|
||||||
case EmailActionType.unMarkAsStarred:
|
|
||||||
_markStar(presentationEmail, MarkStarAction.unMarkStar);
|
|
||||||
break;
|
|
||||||
case EmailActionType.moveToMailbox:
|
|
||||||
_moveEmail(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.moveToTrash:
|
|
||||||
_trashEmail(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.deletePermanently:
|
|
||||||
_deleteEmailPermanently(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.moveToSpam:
|
|
||||||
_markEmailSpam(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.unSpam:
|
|
||||||
_unSpamEmail(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.createRule:
|
|
||||||
_quickCreateRule(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.unsubscribe:
|
|
||||||
_unsubscribeEmail(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.archiveMessage:
|
|
||||||
_archiveMessage(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.printAll:
|
|
||||||
_printEmail(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.downloadMessageAsEML:
|
|
||||||
_downloadMessageAsEML(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.editAsNewEmail:
|
|
||||||
_editAsNewEmail(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.reply:
|
case EmailActionType.reply:
|
||||||
_replyEmail(presentationEmail);
|
_replyEmail(presentationEmail);
|
||||||
break;
|
break;
|
||||||
case EmailActionType.replyAll:
|
|
||||||
_replyAll(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.replyToList:
|
|
||||||
_replyToList(presentationEmail);
|
|
||||||
break;
|
|
||||||
case EmailActionType.forward:
|
|
||||||
_forward(presentationEmail);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -93,212 +34,6 @@ extension ThreadDetailOnEmailActionClick on ThreadDetailController {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _unRead(PresentationEmail presentationEmail) {
|
|
||||||
if (session == null || accountId == null || !presentationEmail.hasRead) return;
|
|
||||||
|
|
||||||
consumeState(emailActionReactor.markAsEmailRead(
|
|
||||||
session!,
|
|
||||||
accountId!,
|
|
||||||
presentationEmail,
|
|
||||||
readAction: ReadActions.markAsUnread,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _markStar(
|
|
||||||
PresentationEmail presentationEmail,
|
|
||||||
MarkStarAction markStarAction,
|
|
||||||
) {
|
|
||||||
if (session == null ||
|
|
||||||
accountId == null ||
|
|
||||||
(presentationEmail.hasStarred && markStarAction == MarkStarAction.markStar) ||
|
|
||||||
(!presentationEmail.hasStarred && markStarAction == MarkStarAction.unMarkStar)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
consumeState(emailActionReactor.markAsStarEmail(
|
|
||||||
session!,
|
|
||||||
accountId!,
|
|
||||||
presentationEmail,
|
|
||||||
markStarAction: markStarAction,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _moveEmail(PresentationEmail presentationEmail) async {
|
|
||||||
if (session == null || accountId == null) return;
|
|
||||||
|
|
||||||
final moveActionRequest = await emailActionReactor.moveToMailbox(
|
|
||||||
session!,
|
|
||||||
accountId!,
|
|
||||||
presentationEmail,
|
|
||||||
mapMailbox: mailboxDashBoardController.mapMailboxById,
|
|
||||||
selectedMailbox: presentationEmail.findMailboxContain(
|
|
||||||
mailboxDashBoardController.mapMailboxById,
|
|
||||||
),
|
|
||||||
isSearchEmailRunning: mailboxDashBoardController.searchController.isSearchEmailRunning,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (moveActionRequest == null) return;
|
|
||||||
|
|
||||||
mailboxDashBoardController.moveToMailbox(
|
|
||||||
session!,
|
|
||||||
accountId!,
|
|
||||||
moveActionRequest.moveRequest,
|
|
||||||
moveActionRequest.emailIdsWithReadStatus,
|
|
||||||
);
|
|
||||||
if (emailIdsPresentation.length == 1) closeThreadDetailAction(currentContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _trashEmail(PresentationEmail presentationEmail) {
|
|
||||||
if (session == null || accountId == null) return;
|
|
||||||
|
|
||||||
final moveActionRequest = emailActionReactor.moveToTrash(
|
|
||||||
presentationEmail,
|
|
||||||
mapMailbox: mailboxDashBoardController.mapMailboxById,
|
|
||||||
selectedMailbox: mailboxDashBoardController.selectedMailbox.value,
|
|
||||||
isSearchEmailRunning: mailboxDashBoardController.searchController.isSearchEmailRunning,
|
|
||||||
mapDefaultMailboxIdByRole: mailboxDashBoardController.mapDefaultMailboxIdByRole,
|
|
||||||
);
|
|
||||||
if (moveActionRequest == null) return;
|
|
||||||
mailboxDashBoardController.moveToMailbox(
|
|
||||||
session!,
|
|
||||||
accountId!,
|
|
||||||
moveActionRequest.moveRequest,
|
|
||||||
moveActionRequest.emailIdsWithReadStatus,
|
|
||||||
);
|
|
||||||
if (emailIdsPresentation.length == 1) closeThreadDetailAction(currentContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _markEmailSpam(PresentationEmail presentationEmail) {
|
|
||||||
if (session == null || accountId == null) return;
|
|
||||||
|
|
||||||
final moveActionRequest = emailActionReactor.moveToSpam(
|
|
||||||
presentationEmail,
|
|
||||||
mapMailbox: mailboxDashBoardController.mapMailboxById,
|
|
||||||
selectedMailbox: mailboxDashBoardController.selectedMailbox.value,
|
|
||||||
isSearchEmailRunning: mailboxDashBoardController.searchController.isSearchEmailRunning,
|
|
||||||
mapDefaultMailboxIdByRole: mailboxDashBoardController.mapDefaultMailboxIdByRole,
|
|
||||||
);
|
|
||||||
if (moveActionRequest == null) return;
|
|
||||||
mailboxDashBoardController.moveToMailbox(
|
|
||||||
session!,
|
|
||||||
accountId!,
|
|
||||||
moveActionRequest.moveRequest,
|
|
||||||
moveActionRequest.emailIdsWithReadStatus,
|
|
||||||
);
|
|
||||||
if (emailIdsPresentation.length == 1) closeThreadDetailAction(currentContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _unSpamEmail(PresentationEmail presentationEmail) {
|
|
||||||
if (session == null || accountId == null) return;
|
|
||||||
|
|
||||||
final moveActionRequest = emailActionReactor.unSpam(
|
|
||||||
presentationEmail,
|
|
||||||
mapMailbox: mailboxDashBoardController.mapMailboxById,
|
|
||||||
selectedMailbox: mailboxDashBoardController.selectedMailbox.value,
|
|
||||||
isSearchEmailRunning: mailboxDashBoardController.searchController.isSearchEmailRunning,
|
|
||||||
mapDefaultMailboxIdByRole: mailboxDashBoardController.mapDefaultMailboxIdByRole,
|
|
||||||
);
|
|
||||||
if (moveActionRequest == null) return;
|
|
||||||
mailboxDashBoardController.moveToMailbox(
|
|
||||||
session!,
|
|
||||||
accountId!,
|
|
||||||
moveActionRequest.moveRequest,
|
|
||||||
moveActionRequest.emailIdsWithReadStatus,
|
|
||||||
);
|
|
||||||
if (emailIdsPresentation.length == 1) closeThreadDetailAction(currentContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _deleteEmailPermanently(PresentationEmail email) {
|
|
||||||
emailActionReactor.deleteEmailPermanently(
|
|
||||||
email,
|
|
||||||
onDeleteEmailRequest: (email) {
|
|
||||||
popBack();
|
|
||||||
mailboxDashBoardController.deleteEmailPermanently(email);
|
|
||||||
},
|
|
||||||
responsiveUtils: responsiveUtils,
|
|
||||||
imagePaths: imagePaths,
|
|
||||||
);
|
|
||||||
if (emailIdsPresentation.length == 1) closeThreadDetailAction(currentContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _quickCreateRule(PresentationEmail presentationEmail) {
|
|
||||||
final emailAddress = presentationEmail.from?.first;
|
|
||||||
if (session == null || accountId == null || emailAddress == null) return;
|
|
||||||
|
|
||||||
consumeState(emailActionReactor.quickCreateRule(
|
|
||||||
session!,
|
|
||||||
accountId!,
|
|
||||||
emailAddress: emailAddress,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _unsubscribeEmail(PresentationEmail presentationEmail) {
|
|
||||||
emailActionReactor.unsubscribeEmail(
|
|
||||||
presentationEmail,
|
|
||||||
emailUnsubscribe: EmailUtils.parsingUnsubscribe(
|
|
||||||
presentationEmail.listUnsubscribe ?? '',
|
|
||||||
),
|
|
||||||
onUnsubscribeByHttpsLink: mailboxDashBoardController.unsubscribeMail,
|
|
||||||
onUnsubscribeByMailtoLink: (emailId, navigationRouter) {
|
|
||||||
mailboxDashBoardController.openComposer(
|
|
||||||
ComposerArguments.fromUnsubscribeMailtoLink(
|
|
||||||
listEmailAddress: navigationRouter.listEmailAddress,
|
|
||||||
subject: navigationRouter.subject,
|
|
||||||
body: navigationRouter.body,
|
|
||||||
previousEmailId: emailId,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _archiveMessage(PresentationEmail presentationEmail) {
|
|
||||||
if (currentContext == null) return;
|
|
||||||
|
|
||||||
emailActionReactor.archiveMessage(
|
|
||||||
presentationEmail,
|
|
||||||
onArchiveEmailRequest: (presentationEmail) {
|
|
||||||
mailboxDashBoardController.archiveMessage(currentContext!, presentationEmail);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _printEmail(PresentationEmail presentationEmail) {
|
|
||||||
consumeState(emailActionReactor.printEmail(
|
|
||||||
presentationEmail,
|
|
||||||
ownEmailAddress: mailboxDashBoardController.ownEmailAddress.value,
|
|
||||||
emailLoaded: null,
|
|
||||||
session: session,
|
|
||||||
accountId: accountId,
|
|
||||||
baseDownloadUrl: session?.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl),
|
|
||||||
transformConfiguration: TransformConfiguration.forPreviewEmailOnWeb(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _downloadMessageAsEML(PresentationEmail presentationEmail) {
|
|
||||||
if (accountId == null || session == null) return;
|
|
||||||
|
|
||||||
consumeState(emailActionReactor.downloadMessageAsEML(
|
|
||||||
session!,
|
|
||||||
accountId!,
|
|
||||||
presentationEmail,
|
|
||||||
downloadProgressStateController: downloadProgressState,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _editAsNewEmail(PresentationEmail presentationEmail) {
|
|
||||||
if (accountId == null || session == null) return;
|
|
||||||
|
|
||||||
emailActionReactor.editAsNewEmail(
|
|
||||||
presentationEmail,
|
|
||||||
onEditAsEmailRequest: (presentationEmail) {
|
|
||||||
mailboxDashBoardController.openComposer(
|
|
||||||
ComposerArguments.editAsNewEmail(presentationEmail),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _replyEmail(PresentationEmail presentationEmail) {
|
void _replyEmail(PresentationEmail presentationEmail) {
|
||||||
emailActionReactor.replyEmail(
|
emailActionReactor.replyEmail(
|
||||||
presentationEmail,
|
presentationEmail,
|
||||||
@@ -318,61 +53,4 @@ extension ThreadDetailOnEmailActionClick on ThreadDetailController {
|
|||||||
emailLoaded: null,
|
emailLoaded: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _replyAll(PresentationEmail presentationEmail) {
|
|
||||||
emailActionReactor.replyAll(
|
|
||||||
presentationEmail,
|
|
||||||
onReplyAllRequest: (presentationEmail, emailLoaded) {
|
|
||||||
mailboxDashBoardController.openComposer(
|
|
||||||
ComposerArguments.replyAllEmail(
|
|
||||||
presentationEmail: presentationEmail,
|
|
||||||
content: emailLoaded?.htmlContent,
|
|
||||||
inlineImages: emailLoaded?.inlineImages,
|
|
||||||
mailboxRole: presentationEmail.mailboxContain?.role,
|
|
||||||
messageId: presentationEmail.messageId,
|
|
||||||
references: presentationEmail.references,
|
|
||||||
listPost: presentationEmail.listPost,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
emailLoaded: null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
void _replyToList(PresentationEmail presentationEmail) {
|
|
||||||
emailActionReactor.replyToList(
|
|
||||||
presentationEmail,
|
|
||||||
onReplyToListRequest: (presentationEmail, emailLoaded) {
|
|
||||||
mailboxDashBoardController.openComposer(
|
|
||||||
ComposerArguments.replyToListEmail(
|
|
||||||
presentationEmail: presentationEmail,
|
|
||||||
content: emailLoaded?.htmlContent,
|
|
||||||
inlineImages: emailLoaded?.inlineImages,
|
|
||||||
mailboxRole: presentationEmail.mailboxContain?.role,
|
|
||||||
messageId: presentationEmail.messageId,
|
|
||||||
references: presentationEmail.references,
|
|
||||||
listPost: presentationEmail.listPost,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
emailLoaded: null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
void _forward(PresentationEmail presentationEmail) {
|
|
||||||
emailActionReactor.forward(
|
|
||||||
presentationEmail,
|
|
||||||
onForwardRequest: (presentationEmail, emailLoaded) {
|
|
||||||
mailboxDashBoardController.openComposer(
|
|
||||||
ComposerArguments.forwardEmail(
|
|
||||||
presentationEmail: presentationEmail,
|
|
||||||
content: emailLoaded?.htmlContent,
|
|
||||||
attachments: emailLoaded?.attachments,
|
|
||||||
inlineImages: emailLoaded?.inlineImages,
|
|
||||||
messageId: presentationEmail.messageId,
|
|
||||||
references: presentationEmail.references,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
emailLoaded: null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+5
@@ -2,6 +2,7 @@ import 'package:dartz/dartz.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:model/email/presentation_email.dart';
|
import 'package:model/email/presentation_email.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
|
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/presentation/extensions/list_presentation_email_extensions.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
|
||||||
@@ -56,6 +57,10 @@ extension ThreadDetailOnSelectedEmailUpdated on ThreadDetailController {
|
|||||||
Right(PreloadEmailIdsInThreadSuccess(
|
Right(PreloadEmailIdsInThreadSuccess(
|
||||||
[selectedEmail.id!],
|
[selectedEmail.id!],
|
||||||
threadId: selectedEmail.threadId,
|
threadId: selectedEmail.threadId,
|
||||||
|
emailsInThreadDetailInfo: [selectedEmail].toEmailsInThreadDetailInfo(
|
||||||
|
sentMailboxId: sentMailboxId,
|
||||||
|
ownEmailAddress: ownEmailAddress,
|
||||||
|
),
|
||||||
)),
|
)),
|
||||||
Right(PreloadEmailsByIdsSuccess([selectedEmail])),
|
Right(PreloadEmailsByIdsSuccess([selectedEmail])),
|
||||||
]));
|
]));
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_
|
|||||||
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_star_email_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/data/local/local_setting_cache_manager.dart';
|
import 'package:tmail_ui_user/features/manage_account/data/local/local_setting_cache_manager.dart';
|
||||||
|
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_detail/data/data_source/thread_detail_data_source.dart';
|
import 'package:tmail_ui_user/features/thread_detail/data/data_source/thread_detail_data_source.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/data/data_source/thread_detail_local_data_source_impl.dart';
|
import 'package:tmail_ui_user/features/thread_detail/data/data_source/thread_detail_local_data_source_impl.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/data/data_source/thread_detail_remote_data_source_impl.dart';
|
import 'package:tmail_ui_user/features/thread_detail/data/data_source/thread_detail_remote_data_source_impl.dart';
|
||||||
@@ -43,6 +45,8 @@ class ThreadDetailBindings extends BaseBindings {
|
|||||||
Get.find<PrintEmailInteractor>(),
|
Get.find<PrintEmailInteractor>(),
|
||||||
Get.find<GetEmailContentInteractor>(),
|
Get.find<GetEmailContentInteractor>(),
|
||||||
Get.find<DownloadAttachmentForWebInteractor>(),
|
Get.find<DownloadAttachmentForWebInteractor>(),
|
||||||
|
Get.find<MarkAsStarMultipleEmailInteractor>(),
|
||||||
|
Get.find<MarkAsMultipleEmailReadInteractor>(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action
|
|||||||
import 'package:tmail_ui_user/features/email/presentation/model/email_loaded.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/email_loaded.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart';
|
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_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_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/usecases/download_attachment_for_web_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_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/mark_as_email_read_interactor.dart';
|
||||||
@@ -37,6 +36,11 @@ import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new
|
|||||||
import 'package:tmail_ui_user/features/network_connection/presentation/network_connection_controller.dart'
|
import 'package:tmail_ui_user/features/network_connection/presentation/network_connection_controller.dart'
|
||||||
if (dart.library.html) 'package:tmail_ui_user/features/network_connection/presentation/web_network_connection_controller.dart';
|
if (dart.library.html) 'package:tmail_ui_user/features/network_connection/presentation/web_network_connection_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/search/email/presentation/search_email_controller.dart';
|
import 'package:tmail_ui_user/features/search/email/presentation/search_email_controller.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_star_multiple_email_state.dart';
|
||||||
|
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_detail/domain/model/email_in_thread_detail_info.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
|
||||||
@@ -46,6 +50,7 @@ import 'package:tmail_ui_user/features/thread_detail/presentation/extension/hand
|
|||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_emails_by_ids_success.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_emails_by_ids_success.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_thread_by_id_failure.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_thread_by_id_failure.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_mark_multiple_emails_read_success.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_refresh_thread_detail_action.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_refresh_thread_detail_action.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/initialize_thread_detail_emails.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/initialize_thread_detail_emails.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/thread_detail_on_selected_email_updated.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/thread_detail_on_selected_email_updated.dart';
|
||||||
@@ -53,7 +58,6 @@ import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_
|
|||||||
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/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_collapsed_email_download_states.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_collapsed_email_download_states.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/mark_collapsed_email_star_success.dart';
|
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/mark_collapsed_email_unread_success.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/mark_collapsed_email_unread_success.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/quick_create_rule_from_collapsed_email_success.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/quick_create_rule_from_collapsed_email_success.dart';
|
||||||
|
|
||||||
@@ -65,6 +69,8 @@ class ThreadDetailController extends BaseController {
|
|||||||
final PrintEmailInteractor _printEmailInteractor;
|
final PrintEmailInteractor _printEmailInteractor;
|
||||||
final GetEmailContentInteractor _getEmailContentInteractor;
|
final GetEmailContentInteractor _getEmailContentInteractor;
|
||||||
final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor;
|
final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor;
|
||||||
|
final MarkAsStarMultipleEmailInteractor markAsStarMultipleEmailInteractor;
|
||||||
|
final MarkAsMultipleEmailReadInteractor markAsMultipleEmailReadInteractor;
|
||||||
|
|
||||||
ThreadDetailController(
|
ThreadDetailController(
|
||||||
this._getEmailIdsByThreadIdInteractor,
|
this._getEmailIdsByThreadIdInteractor,
|
||||||
@@ -74,16 +80,18 @@ class ThreadDetailController extends BaseController {
|
|||||||
this._printEmailInteractor,
|
this._printEmailInteractor,
|
||||||
this._getEmailContentInteractor,
|
this._getEmailContentInteractor,
|
||||||
this._downloadAttachmentForWebInteractor,
|
this._downloadAttachmentForWebInteractor,
|
||||||
|
this.markAsStarMultipleEmailInteractor,
|
||||||
|
this.markAsMultipleEmailReadInteractor,
|
||||||
);
|
);
|
||||||
|
|
||||||
final emailIdsPresentation = <EmailId, PresentationEmail?>{}.obs;
|
final emailIdsPresentation = <EmailId, PresentationEmail?>{}.obs;
|
||||||
final currentExpandedEmailId = Rxn<EmailId>();
|
final currentExpandedEmailId = Rxn<EmailId>();
|
||||||
final currentEmailLoaded = Rxn<EmailLoaded>();
|
final currentEmailLoaded = Rxn<EmailLoaded>();
|
||||||
|
final emailsInThreadDetailInfo = RxList<EmailInThreadDetailInfo>();
|
||||||
|
|
||||||
late final EmailActionReactor emailActionReactor;
|
late final EmailActionReactor emailActionReactor;
|
||||||
final additionalProperties = Properties({
|
final additionalProperties = Properties({
|
||||||
IndividualHeaderIdentifier.listPostHeader.value,
|
IndividualHeaderIdentifier.listPostHeader.value,
|
||||||
IndividualHeaderIdentifier.listUnsubscribeHeader.value,
|
|
||||||
EmailProperty.references,
|
EmailProperty.references,
|
||||||
EmailProperty.messageId,
|
EmailProperty.messageId,
|
||||||
});
|
});
|
||||||
@@ -100,7 +108,6 @@ class ThreadDetailController extends BaseController {
|
|||||||
accountId!,
|
accountId!,
|
||||||
sentMailboxId!,
|
sentMailboxId!,
|
||||||
ownEmailAddress!,
|
ownEmailAddress!,
|
||||||
selectedEmailId: mailboxDashBoardController.selectedEmail.value?.id,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -227,8 +234,18 @@ class ThreadDetailController extends BaseController {
|
|||||||
handleGetEmailsByIdsSuccess(success);
|
handleGetEmailsByIdsSuccess(success);
|
||||||
} else if (success is MarkAsEmailReadSuccess) {
|
} else if (success is MarkAsEmailReadSuccess) {
|
||||||
markCollapsedEmailReadSuccess(success);
|
markCollapsedEmailReadSuccess(success);
|
||||||
} else if (success is MarkAsStarEmailSuccess) {
|
} else if (success is MarkAsMultipleEmailReadAllSuccess) {
|
||||||
markCollapsedEmailStarSuccess(success);
|
handleMarkMultipleEmailsReadSuccess(
|
||||||
|
success,
|
||||||
|
success.readActions,
|
||||||
|
success.emailIds,
|
||||||
|
);
|
||||||
|
} else if (success is MarkAsMultipleEmailReadHasSomeEmailFailure) {
|
||||||
|
handleMarkMultipleEmailsReadSuccess(
|
||||||
|
success,
|
||||||
|
success.readActions,
|
||||||
|
success.successEmailIds,
|
||||||
|
);
|
||||||
} else if (success is CreateNewRuleFilterSuccess) {
|
} else if (success is CreateNewRuleFilterSuccess) {
|
||||||
quickCreateRuleFromCollapsedEmailSuccess(success);
|
quickCreateRuleFromCollapsedEmailSuccess(success);
|
||||||
} else if (success is DownloadAttachmentForWebSuccess) {
|
} else if (success is DownloadAttachmentForWebSuccess) {
|
||||||
@@ -261,6 +278,10 @@ class ThreadDetailController extends BaseController {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (failure is MarkAsMultipleEmailReadFailure ||
|
||||||
|
failure is MarkAsStarMultipleEmailFailure) {
|
||||||
|
toastManager.showMessageFailure(failure as FeatureFailure);
|
||||||
|
}
|
||||||
super.handleFailureViewState(failure);
|
super.handleFailureViewState(failure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,9 +4,10 @@ import 'package:core/utils/platform_info.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:get/get_instance/get_instance.dart';
|
import 'package:get/get_instance/get_instance.dart';
|
||||||
import 'package:get/state_manager.dart';
|
import 'package:get/state_manager.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
import 'package:model/email/presentation_email.dart';
|
import 'package:model/email/presentation_email.dart';
|
||||||
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_detail_status_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_detail_status_state.dart';
|
||||||
@@ -16,7 +17,7 @@ import 'package:tmail_ui_user/features/thread_detail/presentation/extension/init
|
|||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/refresh_thread_detail_on_setting_changed.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/refresh_thread_detail_on_setting_changed.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/model/thread_detail_setting_status.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/model/thread_detail_setting_status.dart';
|
||||||
|
|
||||||
class ThreadDetailManager extends BaseController {
|
class ThreadDetailManager extends ReloadableController {
|
||||||
final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
|
final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
|
||||||
|
|
||||||
final GetThreadDetailStatusInteractor _getThreadDetailStatusInteractor;
|
final GetThreadDetailStatusInteractor _getThreadDetailStatusInteractor;
|
||||||
@@ -86,6 +87,12 @@ class ThreadDetailManager extends BaseController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void handleReloaded(Session session) {
|
||||||
|
super.handleReloaded(session);
|
||||||
|
consumeState(_getThreadDetailStatusInteractor.execute());
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void handleSuccessViewState(Success success) {
|
void handleSuccessViewState(Success success) {
|
||||||
if (success is GetThreadDetailStatusSuccess) {
|
if (success is GetThreadDetailStatusSuccess) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
import 'package:core/presentation/state/failure.dart';
|
import 'package:core/presentation/state/failure.dart';
|
||||||
import 'package:core/presentation/state/success.dart';
|
import 'package:core/presentation/state/success.dart';
|
||||||
@@ -8,19 +7,17 @@ import 'package:dartz/dartz.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:model/extensions/session_extension.dart';
|
import 'package:model/extensions/session_extension.dart';
|
||||||
import 'package:model/email/email_action_type.dart';
|
|
||||||
import 'package:model/mailbox/presentation_mailbox.dart';
|
import 'package:model/mailbox/presentation_mailbox.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
|
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/styles/email_view_app_bar_widget_styles.dart';
|
import 'package:tmail_ui_user/features/email/presentation/styles/email_view_app_bar_widget_styles.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/widgets/email_view_bottom_bar_widget.dart';
|
import 'package:tmail_ui_user/features/email/presentation/widgets/email_view_bottom_bar_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_open_context_menu_extension.dart';
|
|
||||||
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/close_thread_detail_action.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/close_thread_detail_action.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/get_thread_detail_email_mailbox_contains.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/extension/get_thread_details_email_views.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/get_thread_details_email_views.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/on_thread_detail_action_click.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/on_thread_page_changed.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/on_thread_page_changed.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/thread_detail_on_email_action_click.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/features/thread_detail/presentation/widgets/thread_detail_app_bar.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/widgets/thread_detail_app_bar.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
@@ -35,49 +32,18 @@ class ThreadDetailView extends GetWidget<ThreadDetailController> {
|
|||||||
Widget bodyWidget = Column(
|
Widget bodyWidget = Column(
|
||||||
children: [
|
children: [
|
||||||
Obx(() {
|
Obx(() {
|
||||||
final isLoading = showLoadingView(controller.viewState.value);
|
|
||||||
|
|
||||||
return ThreadDetailAppBar(
|
return ThreadDetailAppBar(
|
||||||
responsiveUtils: controller.responsiveUtils,
|
responsiveUtils: controller.responsiveUtils,
|
||||||
imagePaths: controller.imagePaths,
|
imagePaths: controller.imagePaths,
|
||||||
isSearchRunning: controller.isSearchRunning,
|
isSearchRunning: controller.isSearchRunning,
|
||||||
closeThreadDetailAction: controller.closeThreadDetailAction,
|
closeThreadDetailAction: controller.closeThreadDetailAction,
|
||||||
lastEmailOfThread: controller.emailIdsPresentation.values.lastOrNull,
|
|
||||||
ownUserName: controller.session?.getOwnEmailAddress() ?? '',
|
|
||||||
isThreadDetailEnabled: controller.isThreadDetailEnabled,
|
isThreadDetailEnabled: controller.isThreadDetailEnabled,
|
||||||
mailboxContain: _getMailboxContain(),
|
mailboxContain: _getMailboxContain(),
|
||||||
onEmailActionClick: isLoading
|
threadActionReady: controller.emailsInThreadDetailInfo.isNotEmpty &&
|
||||||
? null
|
controller.emailIdsPresentation.length > 1,
|
||||||
: controller.threadDetailOnEmailActionClick,
|
threadDetailIsStarred: controller.threadDetailIsStarred,
|
||||||
onMoreActionClick: (presentationEmail, position) => isLoading
|
onThreadActionClick: controller.onThreadDetailActionClick,
|
||||||
? null
|
onThreadMoreActionClick: controller.onThreadDetailMoreActionClick,
|
||||||
: controller.emailActionReactor.handleMoreEmailAction(
|
|
||||||
mailboxContain: controller.getThreadDetailEmailMailboxContains(
|
|
||||||
presentationEmail,
|
|
||||||
),
|
|
||||||
presentationEmail: presentationEmail,
|
|
||||||
position: position,
|
|
||||||
responsiveUtils: controller.responsiveUtils,
|
|
||||||
imagePaths: controller.imagePaths,
|
|
||||||
username: controller.session?.username,
|
|
||||||
handleEmailAction: controller.threadDetailOnEmailActionClick,
|
|
||||||
additionalActions: [
|
|
||||||
if (controller.responsiveUtils.isMobile(context)) ...[
|
|
||||||
EmailActionType.forward,
|
|
||||||
EmailActionType.replyAll,
|
|
||||||
EmailActionType.replyToList,
|
|
||||||
],
|
|
||||||
EmailActionType.markAsStarred,
|
|
||||||
EmailActionType.unMarkAsStarred,
|
|
||||||
EmailActionType.moveToTrash,
|
|
||||||
EmailActionType.deletePermanently,
|
|
||||||
EmailActionType.printAll,
|
|
||||||
EmailActionType.moveToMailbox,
|
|
||||||
],
|
|
||||||
emailIsRead: presentationEmail.hasRead,
|
|
||||||
openBottomSheetContextMenu: controller.mailboxDashBoardController.openBottomSheetContextMenu,
|
|
||||||
openPopupMenu: controller.mailboxDashBoardController.openPopupMenu,
|
|
||||||
),
|
|
||||||
optionWidgets: [
|
optionWidgets: [
|
||||||
if (controller.previousAvailable)
|
if (controller.previousAvailable)
|
||||||
TMailButtonWidget.fromIcon(
|
TMailButtonWidget.fromIcon(
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import 'dart:math';
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
import 'package:model/email/presentation_email.dart';
|
import 'package:model/email/presentation_email.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/presentation/extensions/list_presentation_email_extensions.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
|
||||||
|
|
||||||
class ThreadDetailPresentationUtils {
|
class ThreadDetailPresentationUtils {
|
||||||
const ThreadDetailPresentationUtils._();
|
const ThreadDetailPresentationUtils._();
|
||||||
@@ -90,4 +93,32 @@ class ThreadDetailPresentationUtils {
|
|||||||
...created,
|
...created,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static List<EmailInThreadDetailInfo> refreshThreadDetailInfo({
|
||||||
|
required List<EmailInThreadDetailInfo> original,
|
||||||
|
required List<PresentationEmail> created,
|
||||||
|
required List<PresentationEmail> updated,
|
||||||
|
required List<EmailId> destroyed,
|
||||||
|
required MailboxId? sentMailboxId,
|
||||||
|
required String? ownEmailAddress,
|
||||||
|
}) {
|
||||||
|
return [
|
||||||
|
...original
|
||||||
|
.whereNot((email) => destroyed.contains(email.emailId))
|
||||||
|
.map((email) {
|
||||||
|
final updatedInOriginal = updated.firstWhereOrNull(
|
||||||
|
(updatedEmail) => updatedEmail.id == email.emailId,
|
||||||
|
);
|
||||||
|
|
||||||
|
return email.copyWith(
|
||||||
|
keywords: updatedInOriginal?.keywords,
|
||||||
|
mailboxIds: updatedInOriginal?.mailboxIds,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
...created.toEmailsInThreadDetailInfo(
|
||||||
|
sentMailboxId: sentMailboxId,
|
||||||
|
ownEmailAddress: ownEmailAddress,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,16 +4,15 @@ import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
|||||||
import 'package:core/utils/platform_info.dart';
|
import 'package:core/utils/platform_info.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:model/email/email_action_type.dart';
|
import 'package:model/email/email_action_type.dart';
|
||||||
import 'package:model/email/presentation_email.dart';
|
|
||||||
import 'package:model/extensions/presentation_email_extension.dart';
|
|
||||||
import 'package:model/mailbox/presentation_mailbox.dart';
|
import 'package:model/mailbox/presentation_mailbox.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/styles/email_view_app_bar_widget_styles.dart';
|
import 'package:tmail_ui_user/features/email/presentation/styles/email_view_app_bar_widget_styles.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email/presentation/widgets/email_view_app_bar_widget.dart';
|
|
||||||
import 'package:tmail_ui_user/features/email/presentation/widgets/email_view_back_button.dart';
|
import 'package:tmail_ui_user/features/email/presentation/widgets/email_view_back_button.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/utils/app_utils.dart';
|
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
||||||
|
|
||||||
|
typedef OnThreadActionClick = void Function(EmailActionType);
|
||||||
|
typedef OnThreadMoreActionClick = void Function(RelativeRect?);
|
||||||
|
|
||||||
class ThreadDetailAppBar extends StatelessWidget {
|
class ThreadDetailAppBar extends StatelessWidget {
|
||||||
const ThreadDetailAppBar({
|
const ThreadDetailAppBar({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -21,33 +20,29 @@ class ThreadDetailAppBar extends StatelessWidget {
|
|||||||
required this.imagePaths,
|
required this.imagePaths,
|
||||||
required this.isSearchRunning,
|
required this.isSearchRunning,
|
||||||
required this.closeThreadDetailAction,
|
required this.closeThreadDetailAction,
|
||||||
required this.lastEmailOfThread,
|
required this.threadActionReady,
|
||||||
required this.ownUserName,
|
required this.threadDetailIsStarred,
|
||||||
required this.isThreadDetailEnabled,
|
required this.isThreadDetailEnabled,
|
||||||
this.mailboxContain,
|
this.mailboxContain,
|
||||||
this.optionWidgets = const [],
|
this.optionWidgets = const [],
|
||||||
this.onEmailActionClick,
|
this.onThreadActionClick,
|
||||||
this.onMoreActionClick,
|
this.onThreadMoreActionClick,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ResponsiveUtils responsiveUtils;
|
final ResponsiveUtils responsiveUtils;
|
||||||
final ImagePaths imagePaths;
|
final ImagePaths imagePaths;
|
||||||
final bool isSearchRunning;
|
final bool isSearchRunning;
|
||||||
final void Function(BuildContext context) closeThreadDetailAction;
|
final void Function(BuildContext context) closeThreadDetailAction;
|
||||||
final PresentationEmail? lastEmailOfThread;
|
final bool threadActionReady;
|
||||||
final String ownUserName;
|
final bool threadDetailIsStarred;
|
||||||
final bool isThreadDetailEnabled;
|
final bool isThreadDetailEnabled;
|
||||||
final PresentationMailbox? mailboxContain;
|
final PresentationMailbox? mailboxContain;
|
||||||
final List<Widget> optionWidgets;
|
final List<Widget> optionWidgets;
|
||||||
final OnEmailActionClick? onEmailActionClick;
|
final OnThreadActionClick? onThreadActionClick;
|
||||||
final OnMoreActionClick? onMoreActionClick;
|
final OnThreadMoreActionClick? onThreadMoreActionClick;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isReplyToListEnabled = EmailUtils.isReplyToListEnabled(
|
|
||||||
lastEmailOfThread?.listPost ?? '',
|
|
||||||
);
|
|
||||||
|
|
||||||
final child = LayoutBuilder(
|
final child = LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
Widget backButton = EmailViewBackButton(
|
Widget backButton = EmailViewBackButton(
|
||||||
@@ -86,61 +81,31 @@ class ThreadDetailAppBar extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
if (_supportDisplayMailboxNameTitle(context)) backButton,
|
if (_supportDisplayMailboxNameTitle(context)) backButton,
|
||||||
if (lastEmailOfThread != null && isThreadDetailEnabled) ...[
|
if (isThreadDetailEnabled && threadActionReady)
|
||||||
_ThreadDetailAppBarButton(
|
_ThreadDetailAppBarButton(
|
||||||
icon: imagePaths.icReply,
|
icon: threadDetailIsStarred
|
||||||
tooltipMessage: AppLocalizations.of(context).reply,
|
? imagePaths.icStar
|
||||||
|
: imagePaths.icUnStar,
|
||||||
|
tooltipMessage: threadDetailIsStarred
|
||||||
|
? AppLocalizations.of(context).not_starred
|
||||||
|
: AppLocalizations.of(context).mark_as_starred,
|
||||||
responsiveUtils: responsiveUtils,
|
responsiveUtils: responsiveUtils,
|
||||||
onTapActionCallback: (_) => onEmailActionClick?.call(
|
iconColor: null,
|
||||||
lastEmailOfThread!,
|
onTapActionCallback: threadDetailIsStarred
|
||||||
EmailActionType.reply,
|
? (_) => onThreadActionClick?.call(EmailActionType.unMarkAsStarred)
|
||||||
),
|
: (_) => onThreadActionClick?.call(EmailActionType.markAsStarred),
|
||||||
),
|
),
|
||||||
if (!responsiveUtils.isMobile(context)) ...[
|
if (!responsiveUtils.isMobile(context)) const Spacer(),
|
||||||
if (lastEmailOfThread!.getCountMailAddressWithoutMe(ownUserName) > 1)
|
|
||||||
_ThreadDetailAppBarButton(
|
|
||||||
icon: imagePaths.icReplyAll,
|
|
||||||
tooltipMessage: AppLocalizations.of(context).reply_all,
|
|
||||||
responsiveUtils: responsiveUtils,
|
|
||||||
onTapActionCallback: (_) => onEmailActionClick?.call(
|
|
||||||
lastEmailOfThread!,
|
|
||||||
EmailActionType.replyAll,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (isReplyToListEnabled)
|
|
||||||
_ThreadDetailAppBarButton(
|
|
||||||
icon: imagePaths.icReply,
|
|
||||||
tooltipMessage: AppLocalizations.of(context).replyToList,
|
|
||||||
responsiveUtils: responsiveUtils,
|
|
||||||
onTapActionCallback: (_) => onEmailActionClick?.call(
|
|
||||||
lastEmailOfThread!,
|
|
||||||
EmailActionType.replyToList,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_ThreadDetailAppBarButton(
|
|
||||||
icon: imagePaths.icForward,
|
|
||||||
tooltipMessage: AppLocalizations.of(context).forward,
|
|
||||||
responsiveUtils: responsiveUtils,
|
|
||||||
onTapActionCallback: (_) => onEmailActionClick?.call(
|
|
||||||
lastEmailOfThread!,
|
|
||||||
EmailActionType.forward,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
if (!responsiveUtils.isMobile(context)) const Spacer(),
|
|
||||||
] else const Spacer(),
|
|
||||||
if (AppUtils.getCurrentDirection(context) == TextDirection.rtl)
|
if (AppUtils.getCurrentDirection(context) == TextDirection.rtl)
|
||||||
...optionWidgets.reversed
|
...optionWidgets.reversed
|
||||||
else
|
else
|
||||||
...optionWidgets,
|
...optionWidgets,
|
||||||
if (lastEmailOfThread != null)
|
if (isThreadDetailEnabled && threadActionReady)
|
||||||
_ThreadDetailAppBarButton(
|
_ThreadDetailAppBarButton(
|
||||||
icon: imagePaths.icMoreVertical,
|
icon: imagePaths.icMoreVertical,
|
||||||
tooltipMessage: AppLocalizations.of(context).more,
|
tooltipMessage: AppLocalizations.of(context).more,
|
||||||
responsiveUtils: responsiveUtils,
|
responsiveUtils: responsiveUtils,
|
||||||
onTapActionCallback: lastEmailOfThread != null
|
onTapActionCallback: onThreadMoreActionClick,
|
||||||
? (position) => onMoreActionClick?.call(lastEmailOfThread!, position)
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -175,22 +140,25 @@ class _ThreadDetailAppBarButton extends StatelessWidget {
|
|||||||
required this.tooltipMessage,
|
required this.tooltipMessage,
|
||||||
required this.onTapActionCallback,
|
required this.onTapActionCallback,
|
||||||
required this.responsiveUtils,
|
required this.responsiveUtils,
|
||||||
|
this.iconColor = EmailViewAppBarWidgetStyles.iconColor,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String icon;
|
final String icon;
|
||||||
final String tooltipMessage;
|
final String tooltipMessage;
|
||||||
final void Function(RelativeRect? position)? onTapActionCallback;
|
final void Function(RelativeRect? position)? onTapActionCallback;
|
||||||
final ResponsiveUtils responsiveUtils;
|
final ResponsiveUtils responsiveUtils;
|
||||||
|
final Color? iconColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final smallScreen = responsiveUtils.isScreenWithShortestSide(context);
|
final smallScreen = responsiveUtils.isScreenWithShortestSide(context);
|
||||||
|
|
||||||
return TMailButtonWidget.fromIcon(
|
return TMailButtonWidget.fromIcon(
|
||||||
icon: icon,
|
icon: icon,
|
||||||
iconSize: EmailViewAppBarWidgetStyles.buttonIconSize,
|
iconSize: EmailViewAppBarWidgetStyles.buttonIconSize,
|
||||||
iconColor: EmailViewAppBarWidgetStyles.iconColor,
|
iconColor: iconColor,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
tooltipMessage: AppLocalizations.of(context).more,
|
tooltipMessage: tooltipMessage,
|
||||||
onTapActionCallback:
|
onTapActionCallback:
|
||||||
smallScreen ? () => onTapActionCallback?.call(null) : null,
|
smallScreen ? () => onTapActionCallback?.call(null) : null,
|
||||||
onTapActionAtPositionCallback: !smallScreen
|
onTapActionAtPositionCallback: !smallScreen
|
||||||
@@ -198,4 +166,4 @@ class _ThreadDetailAppBarButton extends StatelessWidget {
|
|||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ class ThreadDetailCollapsedEmail extends StatelessWidget {
|
|||||||
this.mailboxContain,
|
this.mailboxContain,
|
||||||
this.emailLoaded,
|
this.emailLoaded,
|
||||||
this.onEmailActionClick,
|
this.onEmailActionClick,
|
||||||
this.onMoreActionClick,
|
|
||||||
this.onToggleThreadDetailCollapseExpand,
|
this.onToggleThreadDetailCollapseExpand,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,7 +34,6 @@ class ThreadDetailCollapsedEmail extends StatelessWidget {
|
|||||||
final PresentationMailbox? mailboxContain;
|
final PresentationMailbox? mailboxContain;
|
||||||
final EmailLoaded? emailLoaded;
|
final EmailLoaded? emailLoaded;
|
||||||
final OnEmailActionClick? onEmailActionClick;
|
final OnEmailActionClick? onEmailActionClick;
|
||||||
final OnMoreActionClick? onMoreActionClick;
|
|
||||||
final VoidCallback? onToggleThreadDetailCollapseExpand;
|
final VoidCallback? onToggleThreadDetailCollapseExpand;
|
||||||
|
|
||||||
String get preview => presentationEmail.getPartialContent();
|
String get preview => presentationEmail.getPartialContent();
|
||||||
@@ -69,7 +67,6 @@ class ThreadDetailCollapsedEmail extends StatelessWidget {
|
|||||||
emailLoaded: emailLoaded,
|
emailLoaded: emailLoaded,
|
||||||
isInsideThreadDetailView: true,
|
isInsideThreadDetailView: true,
|
||||||
onEmailActionClick: onEmailActionClick,
|
onEmailActionClick: onEmailActionClick,
|
||||||
onMoreActionClick: onMoreActionClick,
|
|
||||||
openEmailAddressDetailAction: openEmailAddressDetailAction,
|
openEmailAddressDetailAction: openEmailAddressDetailAction,
|
||||||
showRecipients: false,
|
showRecipients: false,
|
||||||
onTapAvatarActionClick: onToggleThreadDetailCollapseExpand,
|
onTapAvatarActionClick: onToggleThreadDetailCollapseExpand,
|
||||||
|
|||||||
+3
@@ -7,6 +7,7 @@ import 'package:mockito/mockito.dart';
|
|||||||
import 'package:model/email/email_in_thread_status.dart';
|
import 'package:model/email/email_in_thread_status.dart';
|
||||||
import 'package:model/email/presentation_email.dart';
|
import 'package:model/email/presentation_email.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success.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';
|
||||||
@@ -62,6 +63,8 @@ void main() {
|
|||||||
};
|
};
|
||||||
when(threadDetailController.emailIdsPresentation)
|
when(threadDetailController.emailIdsPresentation)
|
||||||
.thenReturn(initialEmailIdsPresentation.obs);
|
.thenReturn(initialEmailIdsPresentation.obs);
|
||||||
|
when(threadDetailController.emailsInThreadDetailInfo)
|
||||||
|
.thenReturn(<EmailInThreadDetailInfo>[].obs);
|
||||||
when(threadDetailController.mailboxDashBoardController)
|
when(threadDetailController.mailboxDashBoardController)
|
||||||
.thenReturn(mailboxDashBoardController);
|
.thenReturn(mailboxDashBoardController);
|
||||||
when(mailboxDashBoardController.selectedEmail)
|
when(mailboxDashBoardController.selectedEmail)
|
||||||
|
|||||||
+3
@@ -11,6 +11,7 @@ import 'package:model/email/presentation_email.dart';
|
|||||||
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
|
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
|
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
|
||||||
@@ -98,6 +99,8 @@ void main() {
|
|||||||
.thenReturn(MailboxId(Id('sent-mailbox-id')));
|
.thenReturn(MailboxId(Id('sent-mailbox-id')));
|
||||||
when(threadDetailController.ownEmailAddress)
|
when(threadDetailController.ownEmailAddress)
|
||||||
.thenReturn('9jEYK@example.com');
|
.thenReturn('9jEYK@example.com');
|
||||||
|
when(threadDetailController.emailsInThreadDetailInfo)
|
||||||
|
.thenReturn(<EmailInThreadDetailInfo>[].obs);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
threadDetailController.handleRefreshThreadDetailAction(
|
threadDetailController.handleRefreshThreadDetailAction(
|
||||||
|
|||||||
+9
-1
@@ -8,6 +8,7 @@ import 'package:mockito/annotations.dart';
|
|||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:model/email/presentation_email.dart';
|
import 'package:model/email/presentation_email.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/presentation/extensions/list_presentation_email_extensions.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
|
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
|
||||||
@@ -85,7 +86,14 @@ void main() {
|
|||||||
expect(
|
expect(
|
||||||
streamsConsumed,
|
streamsConsumed,
|
||||||
emitsInOrder([
|
emitsInOrder([
|
||||||
Right(PreloadEmailIdsInThreadSuccess([selectedEmail.id!], threadId: ThreadId(Id('1')))),
|
Right(PreloadEmailIdsInThreadSuccess(
|
||||||
|
[selectedEmail.id!],
|
||||||
|
threadId: ThreadId(Id('1')),
|
||||||
|
emailsInThreadDetailInfo: [selectedEmail].toEmailsInThreadDetailInfo(
|
||||||
|
sentMailboxId: null,
|
||||||
|
ownEmailAddress: null,
|
||||||
|
),
|
||||||
|
)),
|
||||||
Right(PreloadEmailsByIdsSuccess([selectedEmail])),
|
Right(PreloadEmailsByIdsSuccess([selectedEmail])),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|||||||
+244
@@ -4,6 +4,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
|||||||
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
import 'package:model/email/presentation_email.dart';
|
import 'package:model/email/presentation_email.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart';
|
||||||
import 'package:tmail_ui_user/features/thread_detail/presentation/utils/thread_detail_presentation_utils.dart';
|
import 'package:tmail_ui_user/features/thread_detail/presentation/utils/thread_detail_presentation_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@@ -492,5 +493,248 @@ void main() {
|
|||||||
expect(result, isEmpty);
|
expect(result, isEmpty);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('refreshThreadDetailInfo', () {
|
||||||
|
final sentMailboxId = MailboxId(Id('sent'));
|
||||||
|
const ownEmailAddress = 'user@example';
|
||||||
|
test(
|
||||||
|
'should return empty list when all inputs are empty',
|
||||||
|
() {
|
||||||
|
// arrange
|
||||||
|
final original = <EmailInThreadDetailInfo>[];
|
||||||
|
final created = <PresentationEmail>[];
|
||||||
|
final updated = <PresentationEmail>[];
|
||||||
|
final destroyed = <EmailId>[];
|
||||||
|
|
||||||
|
// act
|
||||||
|
final result = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
|
||||||
|
original: original,
|
||||||
|
created: created,
|
||||||
|
updated: updated,
|
||||||
|
destroyed: destroyed,
|
||||||
|
sentMailboxId: sentMailboxId,
|
||||||
|
ownEmailAddress: ownEmailAddress,
|
||||||
|
);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(result, isEmpty);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should return original list when no changes are made',
|
||||||
|
() {
|
||||||
|
// arrange
|
||||||
|
final id = Id('email-1');
|
||||||
|
final emailId = EmailId(id);
|
||||||
|
final original = [
|
||||||
|
EmailInThreadDetailInfo(
|
||||||
|
emailId: emailId,
|
||||||
|
keywords: {},
|
||||||
|
mailboxIds: {},
|
||||||
|
isValidToDisplay: true,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
final created = <PresentationEmail>[];
|
||||||
|
final updated = <PresentationEmail>[];
|
||||||
|
final destroyed = <EmailId>[];
|
||||||
|
|
||||||
|
// act
|
||||||
|
final result = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
|
||||||
|
original: original,
|
||||||
|
created: created,
|
||||||
|
updated: updated,
|
||||||
|
destroyed: destroyed,
|
||||||
|
sentMailboxId: sentMailboxId,
|
||||||
|
ownEmailAddress: ownEmailAddress,
|
||||||
|
);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(result, equals(original));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should return created emails when no original emails are present',
|
||||||
|
() {
|
||||||
|
// arrange
|
||||||
|
final id = Id('email-1');
|
||||||
|
final emailId = EmailId(id);
|
||||||
|
final created = [
|
||||||
|
PresentationEmail(
|
||||||
|
id: emailId,
|
||||||
|
keywords: {},
|
||||||
|
mailboxIds: {},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
final original = <EmailInThreadDetailInfo>[];
|
||||||
|
final updated = <PresentationEmail>[];
|
||||||
|
final destroyed = <EmailId>[];
|
||||||
|
|
||||||
|
// act
|
||||||
|
final result = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
|
||||||
|
original: original,
|
||||||
|
created: created,
|
||||||
|
updated: updated,
|
||||||
|
destroyed: destroyed,
|
||||||
|
sentMailboxId: sentMailboxId,
|
||||||
|
ownEmailAddress: ownEmailAddress,
|
||||||
|
);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(result, hasLength(1));
|
||||||
|
expect(result.first.emailId, equals(emailId));
|
||||||
|
expect(result.first.keywords, equals({}));
|
||||||
|
expect(result.first.mailboxIds, equals({}));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should return updated emails when emails are updated',
|
||||||
|
() {
|
||||||
|
// arrange
|
||||||
|
final id = Id('email-1');
|
||||||
|
final emailId = EmailId(id);
|
||||||
|
final original = [
|
||||||
|
EmailInThreadDetailInfo(
|
||||||
|
emailId: emailId,
|
||||||
|
keywords: {},
|
||||||
|
mailboxIds: {},
|
||||||
|
isValidToDisplay: true,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
final updated = [
|
||||||
|
PresentationEmail(
|
||||||
|
id: emailId,
|
||||||
|
keywords: {KeyWordIdentifier('test'): true},
|
||||||
|
mailboxIds: {},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
final created = <PresentationEmail>[];
|
||||||
|
final destroyed = <EmailId>[];
|
||||||
|
|
||||||
|
// act
|
||||||
|
final result = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
|
||||||
|
original: original,
|
||||||
|
created: created,
|
||||||
|
updated: updated,
|
||||||
|
destroyed: destroyed,
|
||||||
|
sentMailboxId: sentMailboxId,
|
||||||
|
ownEmailAddress: ownEmailAddress,
|
||||||
|
);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(result, hasLength(1));
|
||||||
|
expect(result.first.emailId, equals(emailId));
|
||||||
|
expect(result.first.keywords, equals({KeyWordIdentifier('test'): true}));
|
||||||
|
expect(result.first.mailboxIds, equals({}));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should remove destroyed emails from the result',
|
||||||
|
() {
|
||||||
|
// arrange
|
||||||
|
final id1 = Id('email-1');
|
||||||
|
final emailId1 = EmailId(id1);
|
||||||
|
final id2 = Id('email-2');
|
||||||
|
final emailId2 = EmailId(id2);
|
||||||
|
final original = [
|
||||||
|
EmailInThreadDetailInfo(
|
||||||
|
emailId: emailId1,
|
||||||
|
keywords: {},
|
||||||
|
mailboxIds: {},
|
||||||
|
isValidToDisplay: true,
|
||||||
|
),
|
||||||
|
EmailInThreadDetailInfo(
|
||||||
|
emailId: emailId2,
|
||||||
|
keywords: {},
|
||||||
|
mailboxIds: {},
|
||||||
|
isValidToDisplay: true,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
final created = <PresentationEmail>[];
|
||||||
|
final updated = <PresentationEmail>[];
|
||||||
|
final destroyed = [emailId1];
|
||||||
|
|
||||||
|
// act
|
||||||
|
final result = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
|
||||||
|
original: original,
|
||||||
|
created: created,
|
||||||
|
updated: updated,
|
||||||
|
destroyed: destroyed,
|
||||||
|
sentMailboxId: sentMailboxId,
|
||||||
|
ownEmailAddress: ownEmailAddress,
|
||||||
|
);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(result, hasLength(1));
|
||||||
|
expect(result.first.emailId, equals(emailId2));
|
||||||
|
expect(result.first.keywords, equals({}));
|
||||||
|
expect(result.first.mailboxIds, equals({}));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should apply all changes when multiple changes are present',
|
||||||
|
() {
|
||||||
|
// arrange
|
||||||
|
final id1 = Id('email-1');
|
||||||
|
final emailId1 = EmailId(id1);
|
||||||
|
final id2 = Id('email-2');
|
||||||
|
final emailId2 = EmailId(id2);
|
||||||
|
final id3 = Id('email-3');
|
||||||
|
final emailId3 = EmailId(id3);
|
||||||
|
final original = [
|
||||||
|
EmailInThreadDetailInfo(
|
||||||
|
emailId: emailId1,
|
||||||
|
keywords: {},
|
||||||
|
mailboxIds: {},
|
||||||
|
isValidToDisplay: true,
|
||||||
|
),
|
||||||
|
EmailInThreadDetailInfo(
|
||||||
|
emailId: emailId2,
|
||||||
|
keywords: {},
|
||||||
|
mailboxIds: {},
|
||||||
|
isValidToDisplay: true,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
final created = [
|
||||||
|
PresentationEmail(
|
||||||
|
id: emailId3,
|
||||||
|
keywords: {KeyWordIdentifier('test'): true},
|
||||||
|
mailboxIds: {},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
final updated = [
|
||||||
|
PresentationEmail(
|
||||||
|
id: emailId1,
|
||||||
|
keywords: {KeyWordIdentifier('updated'): true},
|
||||||
|
mailboxIds: {},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
final destroyed = [emailId2];
|
||||||
|
|
||||||
|
// act
|
||||||
|
final result = ThreadDetailPresentationUtils.refreshThreadDetailInfo(
|
||||||
|
original: original,
|
||||||
|
created: created,
|
||||||
|
updated: updated,
|
||||||
|
destroyed: destroyed,
|
||||||
|
sentMailboxId: sentMailboxId,
|
||||||
|
ownEmailAddress: ownEmailAddress,
|
||||||
|
);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(result, hasLength(2));
|
||||||
|
expect(result.first.emailId, equals(emailId1));
|
||||||
|
expect(result.first.keywords, equals({KeyWordIdentifier('updated'): true}));
|
||||||
|
expect(result.first.mailboxIds, equals({}));
|
||||||
|
expect(result.last.emailId, equals(emailId3));
|
||||||
|
expect(result.last.keywords, equals({KeyWordIdentifier('test'): true}));
|
||||||
|
expect(result.last.mailboxIds, equals({}));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user