diff --git a/assets/images/ic_resend_email.svg b/assets/images/ic_resend_email.svg new file mode 100644 index 000000000..8eb540ec1 --- /dev/null +++ b/assets/images/ic_resend_email.svg @@ -0,0 +1,4 @@ + + + + diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index afc1fd8c8..c32296f86 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -185,6 +185,7 @@ class ImagePaths { String get icAvatarPersonal => _getImagePath('ic_avatar_personal.svg'); String get icDialogOfflineMode => _getImagePath('ic_dialog_offline_mode.svg'); String get icMenuMailbox => _getImagePath('ic_menu_mailbox.svg'); + String get icResendEmail => _getImagePath('ic_resend_email.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/lib/features/composer/domain/extensions/list_sending_email_extension.dart b/lib/features/composer/domain/extensions/list_sending_email_extension.dart new file mode 100644 index 000000000..faec960a4 --- /dev/null +++ b/lib/features/composer/domain/extensions/list_sending_email_extension.dart @@ -0,0 +1,14 @@ +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/utc_date.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:model/model.dart'; +import 'package:tmail_ui_user/features/composer/domain/model/sending_email.dart'; + +extension SendingEmailExtension on List { + List toPresentationEmailList() { + return map((sendingEmail) => sendingEmail.email.sendingEmailToPresentationEmail( + emailId: EmailId(Id(sendingEmail.sendingId)), + createAt: UTCDate(sendingEmail.createTime) + )).toList(); + } +} \ No newline at end of file diff --git a/lib/features/composer/domain/extensions/sending_email_extension.dart b/lib/features/composer/domain/extensions/sending_email_extension.dart index 19926d695..38997cbd4 100644 --- a/lib/features/composer/domain/extensions/sending_email_extension.dart +++ b/lib/features/composer/domain/extensions/sending_email_extension.dart @@ -1,4 +1,3 @@ - import 'package:model/extensions/email_extension.dart'; import 'package:model/extensions/email_id_extensions.dart'; import 'package:model/extensions/identity_id_extension.dart'; diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index c821be4ab..6ddbf23a6 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -235,7 +235,9 @@ class ComposerController extends BaseController { @override void onReady() async { _initEmail(); - _getAllIdentities(); + if (isNetworkConnectionAvailable) { + _getAllIdentities(); + } if (!BuildUtils.isWeb) { Future.delayed(const Duration(milliseconds: 500), () => _checkContactPermission()); @@ -1125,8 +1127,9 @@ class ComposerController extends BaseController { } } - if(arguments.emailContents != null && arguments.emailContents!.isNotEmpty) { - emailContentsViewState.value = Right(GetEmailContentFromCacheSuccess(arguments.emailContents!, [])); + if (arguments.emailContents != null && arguments.emailContents!.isNotEmpty) { + _emailContents = arguments.emailContents; + emailContentsViewState.value = Right(GetEmailContentSuccess(_emailContents!, [], [], arguments.presentationEmail?.toEmail())); } else { final session = mailboxDashBoardController.sessionCurrent; final baseDownloadUrl = mailboxDashBoardController.sessionCurrent?.getDownloadUrl(jmapUrl: _dynamicUrlInterceptors.jmapUrl); @@ -1162,11 +1165,9 @@ class ComposerController extends BaseController { success.attachments.listAttachmentsDisplayedOutSide); } emailContentsViewState.value = Right(success); - _emailContents = success.emailContents.asHtmlString; + _emailContents = success.emailContents; } - String? getEmailContentDraftsAsHtml() => _emailContents; - String getEmailAddressSender() { final arguments = composerArguments.value; if (arguments != null) { diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index 6fdf4de01..2bc48ddd3 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -21,7 +21,6 @@ import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:model/email/email_action_type.dart'; import 'package:model/email/prefix_email_address.dart'; -import 'package:model/extensions/list_email_content_extension.dart'; import 'package:model/mailbox/expand_mode.dart'; import 'package:rich_text_composer/rich_text_composer.dart'; import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart'; @@ -628,7 +627,7 @@ class ComposerView extends GetWidget child: loadingWidget, ); } else if (success is GetEmailContentSuccess) { - var contentHtml = success.emailContents.asHtmlString; + var contentHtml = success.emailContents; if (contentHtml.isEmpty == true) { contentHtml = HtmlExtension.editorStartTags; } diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index ddeb6f829..f7f2caacf 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -21,7 +21,6 @@ import 'package:html_editor_enhanced/html_editor.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:model/email/email_action_type.dart'; import 'package:model/email/prefix_email_address.dart'; -import 'package:model/extensions/list_email_content_extension.dart'; import 'package:model/mailbox/expand_mode.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; @@ -773,7 +772,7 @@ class ComposerView extends GetWidget child: loadingWidget, ); } else if (success is GetEmailContentSuccess) { - var contentHtml = success.emailContents.asHtmlString; + var contentHtml = success.emailContents; if (contentHtml.isEmpty == true) { contentHtml = HtmlExtension.editorStartTags; } diff --git a/lib/features/email/domain/state/get_email_content_state.dart b/lib/features/email/domain/state/get_email_content_state.dart index 20b0fb41c..0e19ba6bf 100644 --- a/lib/features/email/domain/state/get_email_content_state.dart +++ b/lib/features/email/domain/state/get_email_content_state.dart @@ -5,7 +5,7 @@ import 'package:model/model.dart'; class GetEmailContentLoading extends LoadingState {} class GetEmailContentSuccess extends UIState { - final List emailContents; + final String emailContents; final List emailContentsDisplayed; final List attachments; final Email? emailCurrent; diff --git a/lib/features/email/domain/usecases/get_email_content_interactor.dart b/lib/features/email/domain/usecases/get_email_content_interactor.dart index f68ceb5e7..5e7ba1cd4 100644 --- a/lib/features/email/domain/usecases/get_email_content_interactor.dart +++ b/lib/features/email/domain/usecases/get_email_content_interactor.dart @@ -8,8 +8,7 @@ import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:model/extensions/email_extension.dart'; -import 'package:model/extensions/list_attachment_extension.dart'; +import 'package:model/model.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; @@ -68,15 +67,15 @@ class GetEmailContentInteractor { : newEmailContents; yield Right(GetEmailContentSuccess( - newEmailContents, + newEmailContents.asHtmlString, newEmailContentsDisplayed, email.allAttachments, email )); } else if (email.allAttachments.isNotEmpty) { - yield Right(GetEmailContentSuccess([], [], email.allAttachments, email)); + yield Right(GetEmailContentSuccess("", [], email.allAttachments, email)); } else if (email.headers?.isNotEmpty == true) { - yield Right(GetEmailContentSuccess([], [], [], email)); + yield Right(GetEmailContentSuccess("", [], [], email)); } else { yield Left(GetEmailContentFailure(null)); } diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 67df14f29..82dcd0e2f 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -371,7 +371,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { emailSupervisorController.popEmailQueue(success.emailCurrent?.id); emailSupervisorController.pushEmailQueue(EmailLoaded( - success.emailContents.toList(), + success.emailContents, success.emailContentsDisplayed.toList(), success.attachments.toList(), success.emailCurrent, @@ -379,7 +379,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { if (success.emailCurrent?.id == currentEmail?.id) { emailContents.value = success.emailContentsDisplayed.asHtmlString; - initialEmailContents = success.emailContents.asHtmlString; + initialEmailContents = success.emailContents; attachments.value = success.attachments; if (!BuildUtils.isWeb) { diff --git a/lib/features/email/presentation/model/email_loaded.dart b/lib/features/email/presentation/model/email_loaded.dart index a35f15b1f..9dbdd7660 100644 --- a/lib/features/email/presentation/model/email_loaded.dart +++ b/lib/features/email/presentation/model/email_loaded.dart @@ -3,7 +3,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/model.dart'; class EmailLoaded with EquatableMixin { - final List emailContents; + final String emailContents; final List emailContentsDisplayed; final List attachments; final Email? emailCurrent; diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart index e1af34905..569bc1939 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart @@ -61,7 +61,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { children: [ SizedBox( width: ResponsiveUtils.defaultSizeLeftMenuMobile, - child: _buildScaffoldHaveDrawer(body: const SendingQueueView())), + child: _buildScaffoldHaveDrawer(body: SendingQueueView())), Expanded(child: EmailView()), ], ); @@ -70,7 +70,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { desktop: bodyView, tabletLarge: bodyView, landscapeTablet: bodyView, - mobile: _buildScaffoldHaveDrawer(body: const SendingQueueView())); + mobile: _buildScaffoldHaveDrawer(body: SendingQueueView())); case DashboardRoutes.waiting: return const Center( child: SizedBox( diff --git a/lib/features/sending_queue/presentation/model/sending_email_action_type.dart b/lib/features/sending_queue/presentation/model/sending_email_action_type.dart index 4ca8e1834..ef240cc0c 100644 --- a/lib/features/sending_queue/presentation/model/sending_email_action_type.dart +++ b/lib/features/sending_queue/presentation/model/sending_email_action_type.dart @@ -4,12 +4,15 @@ import 'package:flutter/cupertino.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; enum SendingEmailActionType { + edit, delete; String getButtonTitle(BuildContext context) { switch(this) { case SendingEmailActionType.delete: return AppLocalizations.of(context).delete; + case SendingEmailActionType.edit: + return AppLocalizations.of(context).edit; } } @@ -17,6 +20,8 @@ enum SendingEmailActionType { switch(this) { case SendingEmailActionType.delete: return 'button_delete_sending_email'; + case SendingEmailActionType.edit: + return 'button_edit_sending_email'; } } @@ -24,6 +29,8 @@ enum SendingEmailActionType { switch(this) { case SendingEmailActionType.delete: return AppColor.colorDeletePermanentlyButton; + case SendingEmailActionType.edit: + return AppColor.primaryColor; } } @@ -31,6 +38,8 @@ enum SendingEmailActionType { switch(this) { case SendingEmailActionType.delete: return AppColor.colorDeletePermanentlyButton; + case SendingEmailActionType.edit: + return AppColor.primaryColor; } } } \ No newline at end of file diff --git a/lib/features/sending_queue/presentation/sending_queue_controller.dart b/lib/features/sending_queue/presentation/sending_queue_controller.dart index c8abdd944..2cb82ac6c 100644 --- a/lib/features/sending_queue/presentation/sending_queue_controller.dart +++ b/lib/features/sending_queue/presentation/sending_queue_controller.dart @@ -8,10 +8,12 @@ import 'package:core/presentation/utils/app_toast.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; +import 'package:model/email/email_action_type.dart'; import 'package:model/mailbox/select_mode.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_mixin.dart'; import 'package:tmail_ui_user/features/composer/domain/model/sending_email.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/offline_mode/scheduler/worker_state.dart'; import 'package:tmail_ui_user/features/sending_queue/presentation/utils/sending_queue_isolate_manager.dart'; @@ -103,6 +105,9 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi case SendingEmailActionType.delete: _deleteSendingEmailAction(context, listSendingEmails); break; + case SendingEmailActionType.edit: + _editSendingEmailAction(listSendingEmails.first); + break; } } @@ -168,6 +173,17 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi } } + void _editSendingEmailAction(SendingEmail sendingEmail) { + final arguments = ComposerArguments( + emailActionType: EmailActionType.edit, + presentationEmail: sendingEmail.presentationEmail, + mailboxRole: dashboardController?.selectedMailbox.value?.role, + emailContents: sendingEmail.email.subject, + ); + + dashboardController?.goToComposer(arguments); + } + void _handleDeleteSendingEmailFailure() { selectionState.value = SelectMode.INACTIVE; } @@ -196,4 +212,55 @@ class SendingQueueController extends BaseController with MessageDialogActionMixi _sendingQueueIsolateManager?.release(); super.onClose(); } + + // + // void pressEmailAction( + // BuildContext context, + // EmailActionType actionType, + // PresentationEmail selectedEmail, + // {PresentationMailbox? mailboxContain} + // ) { + // switch(actionType) { + // case EmailActionType.edit: + // editEmail(selectedEmail); + // break; + // case EmailActionType.selection: + // selectEmail(context, selectedEmail); + // break; + // default: + // break; + // } + // } + // + // void pressEmailSelectionAction( + // BuildContext context, + // EmailActionType actionType, + // List selectionEmail + // ) { + // switch(actionType) { + // case EmailActionType.edit: + // editEmail(selectionEmail.first); + // break; + // default: + // break; + // } + // } + + // bool isSelectionEnabled() => currentSelectMode.value == SelectMode.ACTIVE; + // + // bool? _isUnSelectedAll() { + // return dashboardController?.sendingEmailsInCurrentMailbox.every((email) => email.selectMode == SelectMode.INACTIVE); + // } + // + // void cancelSelectEmail() { + // final newEmailList = dashboardController?.sendingEmailsInCurrentMailbox + // .map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)) + // .toList(); + // updateEmailList(newEmailList); + // currentSelectMode.value = SelectMode.INACTIVE; + // listEmailSelected.clear(); + // } + + // List get listEmailSelected => dashboardController?.sendingEmailsInCurrentMailbox.listEmailSelected ?? []; + } \ No newline at end of file diff --git a/lib/features/sending_queue/presentation/widgets/bottom_bar_sending_queue_widget.dart b/lib/features/sending_queue/presentation/widgets/bottom_bar_sending_queue_widget.dart index 2504f43f2..6f3753acb 100644 --- a/lib/features/sending_queue/presentation/widgets/bottom_bar_sending_queue_widget.dart +++ b/lib/features/sending_queue/presentation/widgets/bottom_bar_sending_queue_widget.dart @@ -35,6 +35,15 @@ class BottomBarSendingQueueWidget extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ + Expanded(child: (ButtonBuilder(imagePaths.icEdit) + ..key(Key(SendingEmailActionType.edit.getButtonKey())) + ..iconColor(SendingEmailActionType.edit.getButtonIconColor()) + ..padding(const EdgeInsets.all(8)) + ..radiusSplash(8) + ..textStyle(TextStyle(fontSize: 12, color: SendingEmailActionType.edit.getButtonTitleColor())) + ..onPressActionClick(() => onHandleSendingEmailActionType?.call(SendingEmailActionType.edit, listSendingEmails)) + ..text(SendingEmailActionType.edit.getButtonTitle(context), isVertical: true) + ).build()), Expanded(child: (ButtonBuilder(imagePaths.icDeleteComposer) ..key(Key(SendingEmailActionType.delete.getButtonKey())) ..iconColor(SendingEmailActionType.delete.getButtonIconColor()) diff --git a/model/lib/email/email_action_type.dart b/model/lib/email/email_action_type.dart index 161ee58fb..860db4769 100644 --- a/model/lib/email/email_action_type.dart +++ b/model/lib/email/email_action_type.dart @@ -17,5 +17,6 @@ enum EmailActionType { selection, moveToSpam, unSpam, - openInNewTab + openInNewTab, + resend } \ No newline at end of file diff --git a/model/lib/email/presentation_email.dart b/model/lib/email/presentation_email.dart index 3cf8a3f55..e491ae02a 100644 --- a/model/lib/email/presentation_email.dart +++ b/model/lib/email/presentation_email.dart @@ -1,6 +1,8 @@ +import 'package:core/domain/extensions/datetime_extension.dart'; import 'package:core/presentation/extensions/string_extension.dart'; import 'package:equatable/equatable.dart'; +import 'package:intl/intl.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/core/utc_date.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; @@ -101,6 +103,10 @@ class PresentationEmail with EquatableMixin { bool get pushNotificationActivated => !isDraft && !hasRead; + String getCreateTimeAt(String newLocale) { + return DateFormat(sentAt?.value.toPattern(), newLocale).format(sentAt?.value ?? DateTime.now()); + } + @override List get props => [ id, diff --git a/model/lib/extensions/email_extension.dart b/model/lib/extensions/email_extension.dart index 48c6c576a..7bd6f34fa 100644 --- a/model/lib/extensions/email_extension.dart +++ b/model/lib/extensions/email_extension.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; +import 'package:jmap_dart_client/jmap/core/utc_date.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart'; import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; @@ -135,4 +136,31 @@ extension EmailExtension on Email { } return null; } + + PresentationEmail sendingEmailToPresentationEmail( + { + SelectMode selectMode = SelectMode.INACTIVE, + EmailId? emailId, + UTCDate? createAt, + } + ) { + return PresentationEmail( + id: emailId ?? id, + keywords: keywords, + size: size, + receivedAt: receivedAt, + hasAttachment: hasAttachment, + preview: preview, + subject: subject, + sentAt: createAt ?? sentAt, + from: from, + to: to, + cc: cc, + bcc: bcc, + replyTo: replyTo, + mailboxIds: mailboxIds, + selectMode: selectMode, + emailHeader: headers?.toList() + ); + } } \ No newline at end of file