From 3c5044c0fcfba2d68609e315dba3fd92b8d61fd8 Mon Sep 17 00:00:00 2001 From: dab246 Date: Fri, 15 Apr 2022 10:10:36 +0700 Subject: [PATCH] TF-484 Hover in email, action buttons are available to do in ThreadView --- .../extensions/color_extension.dart | 1 + .../thread/presentation/thread_bindings.dart | 4 + .../presentation/thread_controller.dart | 191 +++++- .../thread/presentation/thread_view.dart | 17 +- .../widgets/email_tile_builder.dart | 177 +++--- .../widgets/email_tile_web_builder.dart | 551 ++++++++++++++++++ model/lib/email/email_action_type.dart | 2 + 7 files changed, 815 insertions(+), 128 deletions(-) create mode 100644 lib/features/thread/presentation/widgets/email_tile_web_builder.dart diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index 7a9d70fe6..d61619efa 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -109,6 +109,7 @@ extension AppColor on Color { static const colorBorderEmailAddressInvalid = Color(0xFFFF3347); static const colorBgMailboxSelected = Color(0xFF99E4E8EC); static const colorLoading = Color(0x2999A2AD); + static const colorActionButtonHover = Color(0xFFA2AAB3); static const mapGradientColor = [ [Color(0xFF21D4FD), Color(0xFFB721FF)], diff --git a/lib/features/thread/presentation/thread_bindings.dart b/lib/features/thread/presentation/thread_bindings.dart index cf97f158e..41fe38f07 100644 --- a/lib/features/thread/presentation/thread_bindings.dart +++ b/lib/features/thread/presentation/thread_bindings.dart @@ -59,6 +59,8 @@ class ThreadBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), + Get.find(), + Get.find(), )); } @@ -99,6 +101,8 @@ class ThreadBindings extends BaseBindings { Get.lazyPut(() => MoveMultipleEmailToTrashInteractor(Get.find())); Get.lazyPut(() => DeleteMultipleEmailsPermanentlyInteractor(Get.find())); Get.lazyPut(() => EmptyTrashFolderInteractor(Get.find())); + Get.lazyPut(() => MarkAsEmailReadInteractor(Get.find())); + Get.lazyPut(() => MoveToMailboxInteractor(Get.find())); } @override diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 1ed56e87a..ca712a8a8 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -30,7 +30,9 @@ import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_star_sta import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/move_to_trash_state.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/delete_multiple_emails_permanently_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_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/move_to_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart'; @@ -86,6 +88,8 @@ class ThreadController extends BaseController { final MoveMultipleEmailToTrashInteractor _moveMultipleEmailToTrashInteractor; final DeleteMultipleEmailsPermanentlyInteractor _deleteMultipleEmailsPermanentlyInteractor; final EmptyTrashFolderInteractor _emptyTrashFolderInteractor; + final MarkAsEmailReadInteractor _markAsEmailReadInteractor; + final MoveToMailboxInteractor _moveToMailboxInteractor; final emailList = [].obs; final emailListSearch = [].obs; @@ -122,6 +126,8 @@ class ThreadController extends BaseController { this._moveMultipleEmailToTrashInteractor, this._deleteMultipleEmailsPermanentlyInteractor, this._emptyTrashFolderInteractor, + this._markAsEmailReadInteractor, + this._moveToMailboxInteractor, ); @override @@ -218,7 +224,7 @@ class ThreadController extends BaseController { } else if (failure is MarkAsStarMultipleEmailAllFailure || failure is MarkAsStarMultipleEmailFailure) { _markAsStarMultipleEmailFailure(failure); - } else if (failure is EmptyTrashFolderFailure) { + } else if (failure is EmptyTrashFolderFailure) { _emptyTrashFolderFailure(failure); } }, @@ -242,6 +248,10 @@ class ThreadController extends BaseController { _deleteMultipleEmailsPermanentlySuccess(success); } else if (success is EmptyTrashFolderSuccess) { _emptyTrashFolderSuccess(success); + } else if (success is MarkAsEmailReadSuccess) { + _markAsEmailReadSuccess(success); + } else if (success is MoveToMailboxSuccess) { + _moveToMailboxSuccess(success); } } ); @@ -699,11 +709,19 @@ class ThreadController extends BaseController { } } - void markAsStarEmail(PresentationEmail presentationEmail) { - final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value; - if (_accountId != null && mailboxCurrent != null) { - final importantAction = presentationEmail.isFlaggedEmail() ? MarkStarAction.unMarkStar : MarkStarAction.markStar; - consumeState(_markAsStarEmailInteractor.execute(_accountId!, presentationEmail.toEmail(), importantAction)); + void markAsEmailRead(PresentationEmail presentationEmail, ReadActions readActions) async { + if (_accountId != null) { + consumeState(_markAsEmailReadInteractor.execute(_accountId!, presentationEmail.toEmail(), readActions)); + } + } + + void _markAsEmailReadSuccess(Success success) { + mailboxDashBoardController.dispatchState(Right(success)); + } + + void markAsStarEmail(PresentationEmail presentationEmail, MarkStarAction action) { + if (_accountId != null) { + consumeState(_markAsStarEmailInteractor.execute(_accountId!, presentationEmail.toEmail(), action)); } } @@ -713,8 +731,7 @@ class ThreadController extends BaseController { void markAsStarSelectedMultipleEmail(List listPresentationEmail) { final starAction = listPresentationEmail.isAllEmailStarred ? MarkStarAction.unMarkStar : MarkStarAction.markStar; - final mailboxCurrent = mailboxDashBoardController.selectedMailbox.value; - if (_accountId != null && mailboxCurrent != null) { + if (_accountId != null) { cancelSelectEmail(); final listEmail = listPresentationEmail.map((presentationEmail) => presentationEmail.toEmail()).toList(); consumeState(_markAsStarMultipleEmailInteractor.execute(_accountId!, listEmail, starAction)); @@ -835,21 +852,169 @@ class ThreadController extends BaseController { moveSelectedMultipleEmailToTrashAction(selectionEmail); break; case EmailActionType.deletePermanently: - deleteEmailsPermanently(context, DeleteActionType.multiple, selectedEmails: selectionEmail); + deleteSelectionEmailsPermanently(context, DeleteActionType.multiple, selectedEmails: selectionEmail); break; default: break; } } + void pressEmailAction(BuildContext context, EmailActionType actionType, PresentationEmail selectedEmail) { + switch(actionType) { + case EmailActionType.preview: + if (mailboxDashBoardController.selectedMailbox.value?.role == PresentationMailbox.roleDrafts) { + editEmail(selectedEmail); + } else { + previewEmail(context, selectedEmail); + } + break; + case EmailActionType.selection: + selectEmail(context, selectedEmail); + break; + case EmailActionType.markAsRead: + markAsEmailRead(selectedEmail, ReadActions.markAsRead); + break; + case EmailActionType.markAsUnread: + markAsEmailRead(selectedEmail, ReadActions.markAsUnread); + break; + case EmailActionType.markAsStar: + markAsStarEmail(selectedEmail, MarkStarAction.markStar); + break; + case EmailActionType.markAsUnStar: + markAsStarEmail(selectedEmail, MarkStarAction.unMarkStar); + break; + case EmailActionType.move: + moveToMailboxAction(context, selectedEmail); + break; + case EmailActionType.moveToTrash: + moveToTrashAction(selectedEmail); + break; + case EmailActionType.deletePermanently: + deleteEmailPermanently(context, selectedEmail); + break; + default: + break; + } + } + + void moveToMailboxAction(BuildContext context, PresentationEmail email) async { + final currentMailbox = mailboxDashBoardController.selectedMailbox.value; + final accountId = mailboxDashBoardController.accountId.value; + + if (currentMailbox != null && accountId != null) { + final destinationMailbox = await push( + AppRoutes.DESTINATION_PICKER, + arguments: DestinationPickerArguments(accountId, MailboxActions.moveEmail) + ); + + if (destinationMailbox != null && destinationMailbox is PresentationMailbox) { + if (destinationMailbox.role == PresentationMailbox.roleTrash) { + _moveToTrash(accountId, MoveToTrashRequest( + [email.id], + currentMailbox.id, + destinationMailbox.id, + MoveAction.moveToTrash)); + } else { + _moveToMailbox(accountId, MoveRequest( + [email.id], + currentMailbox.id, + destinationMailbox.id, + MoveAction.moveTo, + destinationPath: destinationMailbox.mailboxPath)); + } + } + } + } + + void _moveToMailbox(AccountId accountId, MoveRequest moveRequest) { + consumeState(_moveToMailboxInteractor.execute(accountId, moveRequest)); + } + + void _moveToMailboxSuccess(Success success) { + mailboxDashBoardController.dispatchState(Right(success)); + + if (success is MoveToMailboxSuccess + && success.moveAction == MoveAction.moveTo + && currentContext != null && currentOverlayContext != null) { + _appToast.showToastWithAction( + currentOverlayContext!, + AppLocalizations.of(currentContext!).moved_to_mailbox(success.destinationPath ?? ''), + AppLocalizations.of(currentContext!).undo_action, + () { + final newMoveRequest = MoveRequest( + [success.emailId], + success.destinationMailboxId, + success.currentMailboxId, + MoveAction.undo); + _undoMoveToMailbox(newMoveRequest); + } + ); + } + } + + void _undoMoveToMailbox(MoveRequest newMoveRequest) { + if (_accountId != null) { + _moveToMailbox(_accountId!, newMoveRequest); + } + } + + void _moveToTrash(AccountId accountId, MoveToTrashRequest moveRequest) { + mailboxDashBoardController.moveToTrash(accountId, moveRequest); + } + + void moveToTrashAction(PresentationEmail email) async { + final currentMailbox = mailboxDashBoardController.selectedMailbox.value; + final accountId = mailboxDashBoardController.accountId.value; + final trashMailboxId = mailboxDashBoardController.mapDefaultMailboxId[PresentationMailbox.roleTrash]; + + if (currentMailbox != null && accountId != null && trashMailboxId != null) { + _moveToTrash(accountId, MoveToTrashRequest( + [email.id], + currentMailbox.id, + trashMailboxId, + MoveAction.moveToTrash) + ); + } + } + + void deleteEmailPermanently(BuildContext context, PresentationEmail email) { + if (_responsiveUtils.isMobile(context)) { + (ConfirmationDialogActionSheetBuilder(context) + ..messageText(DeleteActionType.single.getContentDialog(context)) + ..onCancelAction(AppLocalizations.of(context).cancel, () => popBack()) + ..onConfirmAction(DeleteActionType.single.getConfirmActionName(context), () => _deleteEmailPermanentlyAction(context, email))) + .show(); + } else { + showDialog( + context: context, + barrierColor: AppColor.colorDefaultCupertinoActionSheet, + builder: (BuildContext context) => PointerInterceptor(child: (ConfirmDialogBuilder(_imagePaths) + ..key(Key('confirm_dialog_delete_email_permanently')) + ..title(DeleteActionType.single.getTitleDialog(context)) + ..content(DeleteActionType.single.getContentDialog(context)) + ..addIcon(SvgPicture.asset(_imagePaths.icRemoveDialog, fit: BoxFit.fill)) + ..colorConfirmButton(AppColor.colorConfirmActionDialog) + ..styleTextConfirmButton(TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorActionDeleteConfirmDialog)) + ..onCloseButtonAction(() => popBack()) + ..onConfirmButtonAction(DeleteActionType.single.getConfirmActionName(context), () => _deleteEmailPermanentlyAction(context, email)) + ..onCancelButtonAction(AppLocalizations.of(context).cancel, () => popBack())) + .build())); + } + } + + void _deleteEmailPermanentlyAction(BuildContext context, PresentationEmail email) { + popBack(); + mailboxDashBoardController.deleteEmailPermanently(email); + } + bool get isMailboxTrash => mailboxDashBoardController.selectedMailbox.value?.role == PresentationMailbox.roleTrash; - void deleteEmailsPermanently(BuildContext context, DeleteActionType actionType, {List? selectedEmails}) { + void deleteSelectionEmailsPermanently(BuildContext context, DeleteActionType actionType, {List? selectedEmails}) { if (_responsiveUtils.isMobile(context)) { (ConfirmationDialogActionSheetBuilder(context) ..messageText(actionType.getContentDialog(context, count: selectedEmails?.length)) ..onCancelAction(AppLocalizations.of(context).cancel, () => popBack()) - ..onConfirmAction(actionType.getConfirmActionName(context), () => _deleteEmailsPermanentlyAction(actionType))) + ..onConfirmAction(actionType.getConfirmActionName(context), () => _deleteSelectionEmailsPermanentlyAction(actionType))) .show(); } else { showDialog( @@ -863,13 +1028,13 @@ class ThreadController extends BaseController { ..colorConfirmButton(AppColor.colorConfirmActionDialog) ..styleTextConfirmButton(TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorActionDeleteConfirmDialog)) ..onCloseButtonAction(() => popBack()) - ..onConfirmButtonAction(actionType.getConfirmActionName(context), () => _deleteEmailsPermanentlyAction(actionType)) + ..onConfirmButtonAction(actionType.getConfirmActionName(context), () => _deleteSelectionEmailsPermanentlyAction(actionType)) ..onCancelButtonAction(AppLocalizations.of(context).cancel, () => popBack())) .build())); } } - void _deleteEmailsPermanentlyAction(DeleteActionType actionType) { + void _deleteSelectionEmailsPermanentlyAction(DeleteActionType actionType) { popBack(); switch(actionType) { diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 1f96bd39e..444c040ad 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -13,7 +13,8 @@ import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_t import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart'; -import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_builder.dart'; +import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_builder.dart' + if (dart.library.html) 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_web_builder.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/search_app_bar_widget.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/suggestion_box_widget.dart'; @@ -223,7 +224,7 @@ class ThreadView extends GetWidget with UserSettingPopupMenuMi : AppLocalizations.of(context).delete_permanently, onTap: () => controller.mailboxDashBoardController.selectedMailbox.value?.role != PresentationMailbox.roleTrash ? controller.pressEmailSelectionAction(context, EmailActionType.moveToTrash, controller.listEmailSelected) - : controller.deleteEmailsPermanently(context, DeleteActionType.multiple, selectedEmails: controller.listEmailSelected)), + : controller.deleteSelectionEmailsPermanently(context, DeleteActionType.multiple, selectedEmails: controller.listEmailSelected)), ]); } @@ -597,15 +598,7 @@ class ThreadView extends GetWidget with UserSettingPopupMenuMi controller.currentSelectMode.value, controller.mailboxDashBoardController.searchState.value.searchStatus, controller.searchQuery) - ..onOpenEmailAction((selectedEmail) { - if (controller.mailboxDashBoardController.selectedMailbox.value?.role == PresentationMailbox.roleDrafts) { - controller.editEmail(selectedEmail); - } else { - controller.previewEmail(context, selectedEmail); - } - }) - ..onSelectEmailAction((selectedEmail) => controller.selectEmail(context, selectedEmail)) - ..addOnMarkAsStarActionClick((selectedEmail) => controller.markAsStarEmail(selectedEmail))) + ..addOnPressEmailActionClick((action, email) => controller.pressEmailAction(context, action, email))) .build()), ) ); @@ -688,7 +681,7 @@ class ThreadView extends GetWidget with UserSettingPopupMenuMi AppLocalizations.of(context).message_delete_all_email_in_trash_button, style: TextStyle(color: AppColor.colorContentEmail, fontSize: 13, fontWeight: FontWeight.w500))), TextButton( - onPressed: () => controller.deleteEmailsPermanently(context, DeleteActionType.all), + onPressed: () => controller.deleteSelectionEmailsPermanently(context, DeleteActionType.all), child: Text( AppLocalizations.of(context).empty_trash_now, style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorTextButton) diff --git a/lib/features/thread/presentation/widgets/email_tile_builder.dart b/lib/features/thread/presentation/widgets/email_tile_builder.dart index a36847ee8..03663984a 100644 --- a/lib/features/thread/presentation/widgets/email_tile_builder.dart +++ b/lib/features/thread/presentation/widgets/email_tile_builder.dart @@ -1,5 +1,4 @@ import 'package:core/core.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; @@ -9,9 +8,7 @@ import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/search_status.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -typedef OnOpenEmailActionClick = void Function(PresentationEmail selectedEmail); -typedef OnSelectEmailActionClick = void Function(PresentationEmail selectedEmail); -typedef OnMarkAsStarActionClick = void Function(PresentationEmail selectedEmail); +typedef OnPressEmailActionClick = void Function(EmailActionType, PresentationEmail); class EmailTileBuilder { @@ -25,9 +22,10 @@ class EmailTileBuilder { final SearchStatus _searchStatus; final SearchQuery? _searchQuery; - OnOpenEmailActionClick? _onOpenEmailActionClick; - OnSelectEmailActionClick? _onSelectEmailActionClick; - OnMarkAsStarActionClick? _onMarkAsStarActionClick; + OnPressEmailActionClick? _emailActionClick; + + bool isHoverItem = false; + bool isHoverItemSelected = false; EmailTileBuilder( this._context, @@ -38,16 +36,8 @@ class EmailTileBuilder { this._searchQuery, ); - void onOpenEmailAction(OnOpenEmailActionClick onOpenEmailActionClick) { - _onOpenEmailActionClick = onOpenEmailActionClick; - } - - void onSelectEmailAction(OnSelectEmailActionClick onSelectEmailActionClick) { - _onSelectEmailActionClick = onSelectEmailActionClick; - } - - void addOnMarkAsStarActionClick(OnMarkAsStarActionClick onMarkAsStarActionClick) { - _onMarkAsStarActionClick = onMarkAsStarActionClick; + void addOnPressEmailActionClick(OnPressEmailActionClick actionClick) { + _emailActionClick = actionClick; } Widget build() { @@ -73,78 +63,64 @@ class EmailTileBuilder { Widget _buildListTile() { return Container( - margin: kIsWeb && _selectModeAll == SelectMode.ACTIVE - ? EdgeInsets.only(top: 3, left: 16, right: 16) - : EdgeInsets.zero, - padding: kIsWeb && _selectModeAll == SelectMode.ACTIVE - ? EdgeInsets.only(top: 8, bottom: 16, right: 8) - : EdgeInsets.only(bottom: 10, left: 16, right: 16), - decoration: kIsWeb && _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE - ? BoxDecoration(borderRadius: BorderRadius.circular(14), color: AppColor.colorItemEmailSelectedDesktop) - : BoxDecoration(borderRadius: BorderRadius.circular(0), color: Colors.white), + margin: EdgeInsets.zero, + padding: EdgeInsets.only(bottom: 10, left: 16, right: 16), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(0), color: Colors.white), alignment: Alignment.center, child: Column(children: [ ListTile( contentPadding: EdgeInsets.zero, - onTap: () => _onOpenEmailActionClick?.call(_presentationEmail), - onLongPress: () => _onSelectEmailActionClick!.call(_presentationEmail), + onTap: () => _emailActionClick?.call(EmailActionType.preview, _presentationEmail), + onLongPress: () => _emailActionClick?.call(EmailActionType.selection, _presentationEmail), leading: GestureDetector( - onTap: () { - if (_selectModeAll == SelectMode.ACTIVE) { - _onSelectEmailActionClick?.call(_presentationEmail); - } else { - _onOpenEmailActionClick?.call(_presentationEmail); - } - }, + onTap: () => _emailActionClick?.call( + _selectModeAll == SelectMode.ACTIVE ? EmailActionType.selection : EmailActionType.preview, + _presentationEmail), child: _buildAvatarIcon(), ), - title: Transform( - transform: Matrix4.translationValues(0.0, 0.0, 0.0), - child: Row( - children: [ - if (_presentationEmail.isUnReadEmail()) - Padding( - padding: EdgeInsets.only(right: 5), - child: SvgPicture.asset(_imagePaths.icUnreadStatus, width: 9, height: 9, fit: BoxFit.fill)), - Expanded( - child: _searchStatus == SearchStatus.ACTIVE && _searchQuery != null && _searchQuery!.value.isNotEmpty - ? RichTextBuilder( - _getInformationSender(), - '${_searchQuery!.value}', - TextStyle(fontSize: 15, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600), - TextStyle(fontSize: 15, color: AppColor.colorNameEmail, backgroundColor: AppColor.bgWordSearch, fontWeight: FontWeight.w600)).build() - : Text( - _getInformationSender(), - maxLines: 1, - overflow:TextOverflow.ellipsis, - style: TextStyle(fontSize: 15, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600)) - ), - if (_presentationEmail.hasAttachment == true) - Padding( - padding: EdgeInsets.only(left: 8), - child: (ButtonBuilder(_imagePaths.icAttachment) - ..paddingIcon(EdgeInsets.zero) - ..size(16)) - .build()), - Padding( - padding: EdgeInsets.only(right: 4, left: 8), - child: Text( - '${_presentationEmail.getReceivedAt(Localizations.localeOf(_context).toLanguageTag())}', + title: Row( + children: [ + if (_presentationEmail.isUnReadEmail()) + Padding( + padding: EdgeInsets.only(right: 5), + child: SvgPicture.asset(_imagePaths.icUnreadStatus, width: 9, height: 9, fit: BoxFit.fill)), + Expanded( + child: _searchStatus == SearchStatus.ACTIVE && _searchQuery != null && _searchQuery!.value.isNotEmpty + ? RichTextBuilder( + _getInformationSender(), + '${_searchQuery!.value}', + TextStyle(fontSize: 15, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600), + TextStyle(fontSize: 15, color: AppColor.colorNameEmail, backgroundColor: AppColor.bgWordSearch, fontWeight: FontWeight.w600)).build() + : Text( + _getInformationSender(), maxLines: 1, overflow:TextOverflow.ellipsis, - style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail))), - (ButtonBuilder(_imagePaths.icChevron) - ..paddingIcon(EdgeInsets.zero) - ..size(16)) - .build(), - ], - ) + style: TextStyle(fontSize: 15, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600)) + ), + if (_presentationEmail.hasAttachment == true) + Padding( + padding: EdgeInsets.only(left: 8), + child: (ButtonBuilder(_imagePaths.icAttachment) + ..paddingIcon(EdgeInsets.zero) + ..size(16)) + .build()), + Padding( + padding: EdgeInsets.only(right: 4, left: 8), + child: Text( + '${_presentationEmail.getReceivedAt(Localizations.localeOf(_context).toLanguageTag())}', + maxLines: 1, + overflow:TextOverflow.ellipsis, + style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail))), + (ButtonBuilder(_imagePaths.icChevron) + ..paddingIcon(EdgeInsets.zero) + ..size(16)) + .build(), + ], ), subtitle: Transform( transform: Matrix4.translationValues(0.0, 8.0, 0.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Padding( @@ -198,30 +174,26 @@ class EmailTileBuilder { : Text( '${_presentationEmail.getPartialContent()}', maxLines: 1, - overflow:TextOverflow.ellipsis, + overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail)) ), - ])), - if (kIsWeb && _selectModeAll == SelectMode.INACTIVE) - Padding( - padding: EdgeInsets.only(top: 12), - child: Divider(color: AppColor.lineItemListColor, height: 1, thickness: 0.2)), + ]) + ), ], ) ), ), - if (!kIsWeb) - Padding( - padding: EdgeInsets.only(top: 12, right: 12), - child: Divider(color: AppColor.lineItemListColor, height: 1, thickness: 0.2)), + Padding( + padding: EdgeInsets.only(top: 12, right: 12), + child: Divider(color: AppColor.lineItemListColor, height: 1, thickness: 0.2)), ]), ); } Widget _buildListTileForDesktop() { return InkWell( - onTap: () => _onOpenEmailActionClick?.call(_presentationEmail), - onLongPress: () => _onSelectEmailActionClick?.call(_presentationEmail), + onTap: () => _emailActionClick?.call(EmailActionType.preview, _presentationEmail), + onLongPress: () => _emailActionClick?.call(EmailActionType.selection, _presentationEmail), child: Column(children: [ Row(children: [ Container( @@ -237,16 +209,14 @@ class EmailTileBuilder { height: 20, fit: BoxFit.fill), tooltip: _presentationEmail.isFlaggedEmail() ? AppLocalizations.of(_context).starred : AppLocalizations.of(_context).not_starred, - onTap: () => _onMarkAsStarActionClick?.call(_presentationEmail)), + onTap: () => _emailActionClick?.call( + _presentationEmail.isFlaggedEmail() ? EmailActionType.markAsUnStar : EmailActionType.markAsStar, + _presentationEmail)), if (_selectModeAll == SelectMode.INACTIVE) SizedBox(width: 8), GestureDetector( - onTap: () { - if (_selectModeAll == SelectMode.ACTIVE) { - _onSelectEmailActionClick?.call(_presentationEmail); - } else { - _onOpenEmailActionClick?.call(_presentationEmail); - } - }, + onTap: () => _emailActionClick?.call( + _selectModeAll == SelectMode.ACTIVE ? EmailActionType.selection : EmailActionType.preview, + _presentationEmail), child: _buildAvatarIcon( iconSize: 32, textStyle: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Colors.white), @@ -263,7 +233,7 @@ class EmailTileBuilder { TextStyle(fontSize: 15, color: AppColor.colorNameEmail, backgroundColor: AppColor.bgWordSearch, fontWeight: FontWeight.w600)).build() : Text('${_getInformationSender()}', maxLines: 1, - overflow: kIsWeb ? null : TextOverflow.ellipsis, + overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 15, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600)), ), SizedBox(width: 24), @@ -277,7 +247,7 @@ class EmailTileBuilder { decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: AppColor.backgroundCounterMailboxColor), child: Text('${_presentationEmail.mailboxName}', maxLines: 1, - overflow: kIsWeb ? null : TextOverflow.ellipsis, + overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 10, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold), ) ), @@ -292,7 +262,7 @@ class EmailTileBuilder { child: Text('${_presentationEmail.getReceivedAt(Localizations.localeOf(_context).toLanguageTag())}', maxLines: 1, textAlign: TextAlign.right, - overflow: kIsWeb ? null : TextOverflow.ellipsis, + overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail, fontWeight: FontWeight.normal))), ]), if (_selectModeAll == SelectMode.INACTIVE) @@ -305,7 +275,6 @@ class EmailTileBuilder { Widget _buildSubjectAndContent() { return LayoutBuilder(builder: (context, constraints) { - log('EmailTileBuilder::_buildSubjectAndContent(): maxWidth(Subject+Content): ${constraints.maxWidth}'); return Row(children: [ Container( constraints: BoxConstraints(maxWidth: constraints.maxWidth / 2), @@ -317,7 +286,7 @@ class EmailTileBuilder { TextStyle(fontSize: 13, backgroundColor: AppColor.bgWordSearch, color: AppColor.colorNameEmail)).build() : Text('${_presentationEmail.getEmailTitle()}', maxLines: 1, - overflow: kIsWeb ? null : TextOverflow.ellipsis, + overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 13, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600)) ), if (_presentationEmail.getEmailTitle().isNotEmpty) SizedBox(width: 12), @@ -331,7 +300,7 @@ class EmailTileBuilder { TextStyle(fontSize: 13, color: AppColor.colorContentEmail, backgroundColor: AppColor.bgWordSearch)).build() : Text('${_presentationEmail.getPartialContent()}', maxLines: 1, - overflow: kIsWeb ? null : TextOverflow.ellipsis, + overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail, fontWeight: FontWeight.normal)) ) ), @@ -353,7 +322,7 @@ class EmailTileBuilder { } else { return (AvatarBuilder() ..text('${_presentationEmail.getAvatarText()}') - ..size(iconSize ?? (GetPlatform.isWeb ? 48 : 56)) + ..size(iconSize ?? 56) ..textColor(Colors.white) ..addTextStyle(textStyle) ..avatarColor(_presentationEmail.avatarColors)) @@ -370,5 +339,7 @@ class EmailTileBuilder { return '${_presentationEmail.getSenderName()}'; } - bool get _isSearchEnabled => _searchStatus == SearchStatus.ACTIVE && _searchQuery != null && _searchQuery!.value.isNotEmpty; + bool get _isSearchEnabled => _searchStatus == SearchStatus.ACTIVE + && _searchQuery != null + && _searchQuery!.value.isNotEmpty; } \ No newline at end of file diff --git a/lib/features/thread/presentation/widgets/email_tile_web_builder.dart b/lib/features/thread/presentation/widgets/email_tile_web_builder.dart new file mode 100644 index 000000000..0070f9a80 --- /dev/null +++ b/lib/features/thread/presentation/widgets/email_tile_web_builder.dart @@ -0,0 +1,551 @@ +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:model/model.dart'; +import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/search_status.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +typedef OnPressEmailActionClick = void Function(EmailActionType, PresentationEmail); + +class EmailTileBuilder { + + final _responsiveUtils = Get.find(); + final _imagePaths = Get.find(); + + final PresentationEmail _presentationEmail; + final BuildContext _context; + final SelectMode _selectModeAll; + final Role? _mailboxRole; + final SearchStatus _searchStatus; + final SearchQuery? _searchQuery; + + OnPressEmailActionClick? _emailActionClick; + + bool isHoverItem = false; + + EmailTileBuilder( + this._context, + this._presentationEmail, + this._mailboxRole, + this._selectModeAll, + this._searchStatus, + this._searchQuery, + ); + + void addOnPressEmailActionClick(OnPressEmailActionClick actionClick) { + _emailActionClick = actionClick; + } + + Widget build() { + return Theme( + key: Key('thread_tile'), + data: ThemeData(splashColor: Colors.transparent, highlightColor: Colors.transparent), + child: ResponsiveWidget( + responsiveUtils: _responsiveUtils, + mobile: Container( + margin: _selectModeAll == SelectMode.ACTIVE + ? EdgeInsets.only(top: 3, left: 16, right: 16) + : EdgeInsets.zero, + padding: _selectModeAll == SelectMode.ACTIVE + ? EdgeInsets.only(top: 8, bottom: 16, right: 8) + : EdgeInsets.only(bottom: 10, left: 16, right: 16), + decoration: _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE + ? BoxDecoration(borderRadius: BorderRadius.circular(14), color: AppColor.colorItemEmailSelectedDesktop) + : BoxDecoration(borderRadius: BorderRadius.circular(0), color: Colors.white), + alignment: Alignment.center, + child: _buildListTile()), + tablet: Container( + margin: _selectModeAll == SelectMode.ACTIVE + ? EdgeInsets.only(top: 3, left: 16, right: 16) + : EdgeInsets.zero, + padding: _selectModeAll == SelectMode.ACTIVE + ? EdgeInsets.only(top: 8, bottom: 16, right: 8) + : EdgeInsets.only(bottom: 10, left: 16, right: 16), + decoration: _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE + ? BoxDecoration(borderRadius: BorderRadius.circular(14), color: AppColor.colorItemEmailSelectedDesktop) + : BoxDecoration(borderRadius: BorderRadius.circular(0), color: Colors.white), + alignment: Alignment.center, + child: _buildListTileTabletLarge()), + tabletLarge: Container( + margin: _selectModeAll == SelectMode.ACTIVE + ? EdgeInsets.only(top: 3, left: 16, right: 16) + : EdgeInsets.zero, + padding: _selectModeAll == SelectMode.ACTIVE + ? EdgeInsets.only(top: 8, bottom: 16, right: 8) + : EdgeInsets.only(bottom: 10, left: 16, right: 16), + decoration: _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE + ? BoxDecoration(borderRadius: BorderRadius.circular(14), color: AppColor.colorItemEmailSelectedDesktop) + : BoxDecoration(borderRadius: BorderRadius.circular(0), color: Colors.white), + alignment: Alignment.center, + child: _buildListTileTabletLarge()), + desktop: Container( + margin: _selectModeAll == SelectMode.ACTIVE ? EdgeInsets.only(top: 3, left: 8, right: 8) : EdgeInsets.zero, + padding: _selectModeAll == SelectMode.ACTIVE ? EdgeInsets.symmetric(vertical: 8) : EdgeInsets.zero, + decoration: _selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE + ? BoxDecoration( + borderRadius: BorderRadius.circular(14), + color: AppColor.colorItemEmailSelectedDesktop) + : null, + child: _buildListTileForDesktop(), + ), + ) + ); + } + + Widget _buildListTile() { + return Column(children: [ + ListTile( + contentPadding: EdgeInsets.zero, + onTap: () => _emailActionClick?.call(EmailActionType.preview, _presentationEmail), + onLongPress: () => _emailActionClick?.call(EmailActionType.selection, _presentationEmail), + leading: GestureDetector( + onTap: () => _emailActionClick?.call( + _selectModeAll == SelectMode.ACTIVE ? EmailActionType.selection : EmailActionType.preview, + _presentationEmail), + child: _buildAvatarIcon(), + ), + title: Row(children: [ + if (_presentationEmail.isUnReadEmail()) + Padding( + padding: EdgeInsets.only(right: 5), + child: SvgPicture.asset(_imagePaths.icUnreadStatus, width: 9, height: 9, fit: BoxFit.fill)), + Expanded( + child: _searchStatus == SearchStatus.ACTIVE && _searchQuery != null && _searchQuery!.value.isNotEmpty + ? RichTextBuilder( + _getInformationSender(), + '${_searchQuery!.value}', + TextStyle(fontSize: 15, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600), + TextStyle(fontSize: 15, color: AppColor.colorNameEmail, backgroundColor: AppColor.bgWordSearch, fontWeight: FontWeight.w600)).build() + : Text( + _getInformationSender(), + maxLines: 1, + style: TextStyle(fontSize: 15, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600)) + ), + if (_presentationEmail.hasAttachment == true) + Padding( + padding: EdgeInsets.only(left: 8), + child: (ButtonBuilder(_imagePaths.icAttachment) + ..paddingIcon(EdgeInsets.zero) + ..size(16)) + .build()), + Padding( + padding: EdgeInsets.only(right: 4, left: 8), + child: Text( + '${_presentationEmail.getReceivedAt(Localizations.localeOf(_context).toLanguageTag())}', + maxLines: 1, + style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail))), + (ButtonBuilder(_imagePaths.icChevron) + ..paddingIcon(EdgeInsets.zero) + ..size(16)) + .build(), + ]), + subtitle: Transform( + transform: Matrix4.translationValues(0.0, 8.0, 0.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: EdgeInsets.zero, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded(child: _searchStatus == SearchStatus.ACTIVE && _searchQuery != null && _searchQuery!.value.isNotEmpty + ? RichTextBuilder( + '${_presentationEmail.getEmailTitle()}', + '${_searchQuery!.value}', + TextStyle(fontSize: 13, color: AppColor.colorNameEmail), + TextStyle(fontSize: 13, backgroundColor: AppColor.bgWordSearch, color: AppColor.colorNameEmail)).build() + : Text( + '${_presentationEmail.getEmailTitle()}', + maxLines: 1, + style: TextStyle(fontSize: 13, color: AppColor.colorNameEmail)) + ), + if (_searchStatus == SearchStatus.ACTIVE && _presentationEmail.mailboxName.isNotEmpty) + Container( + margin: EdgeInsets.only(left: 8), + padding: EdgeInsets.only(left: 8, right: 8, top: 3, bottom: 3), + constraints: BoxConstraints(maxWidth: 100), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: AppColor.backgroundCounterMailboxColor), + child: Text( + '${_presentationEmail.mailboxName}', + maxLines: 1, + style: TextStyle(fontSize: 10, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold), + ) + ), + if (_presentationEmail.isFlaggedEmail() ) + (ButtonBuilder(_imagePaths.icStar) + ..paddingIcon(EdgeInsets.zero) + ..size(15)) + .build(), + ], + )), + Padding( + padding: EdgeInsets.only(top: 8), + child: Row(children: [ + Expanded(child: _searchStatus == SearchStatus.ACTIVE && _searchQuery != null && _searchQuery!.value.isNotEmpty + ? RichTextBuilder( + '${_presentationEmail.getPartialContent()}', + '${_searchQuery!.value}', + TextStyle(fontSize: 13, color: AppColor.colorContentEmail), + TextStyle(fontSize: 13, color: AppColor.colorContentEmail, backgroundColor: AppColor.bgWordSearch)).build() + : Text( + '${_presentationEmail.getPartialContent()}', + maxLines: 1, + style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail)) + ), + ])), + if (_selectModeAll == SelectMode.INACTIVE) + Padding( + padding: EdgeInsets.only(top: 12), + child: Divider(color: AppColor.lineItemListColor, height: 1, thickness: 0.2)), + ], + ) + ), + ), + ]); + } + + Widget _buildListTileTabletLarge() { + return StatefulBuilder(builder: (BuildContext context, StateSetter setState) { + return InkWell( + onTap: () => _emailActionClick?.call(EmailActionType.preview, _presentationEmail), + onLongPress: () => _emailActionClick?.call(EmailActionType.selection, _presentationEmail), + onHover: (value) => setState(() => isHoverItem = value), + child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Padding( + padding: EdgeInsets.only(top: 8), + child: GestureDetector( + onTap: () => _emailActionClick?.call( + _selectModeAll == SelectMode.ACTIVE ? EmailActionType.selection : EmailActionType.preview, + _presentationEmail), + child: _buildAvatarIcon())), + SizedBox(width: 12), + Expanded(child: Column(children: [ + Transform( + transform: Matrix4.translationValues(0.0, isHoverItem ? -10.0 : 0.0, 0.0), + child: Row(children: [ + if (_presentationEmail.isUnReadEmail()) + Padding( + padding: EdgeInsets.only(right: 5), + child: SvgPicture.asset(_imagePaths.icUnreadStatus, width: 9, height: 9, fit: BoxFit.fill)), + Expanded( + child: _searchStatus == SearchStatus.ACTIVE && _searchQuery != null && _searchQuery!.value.isNotEmpty + ? RichTextBuilder( + _getInformationSender(), + '${_searchQuery!.value}', + TextStyle(fontSize: 15, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600), + TextStyle(fontSize: 15, color: AppColor.colorNameEmail, backgroundColor: AppColor.bgWordSearch, fontWeight: FontWeight.w600)).build() + : Text( + _getInformationSender(), + maxLines: 1, + style: TextStyle(fontSize: 15, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600)) + ), + if (isHoverItem) + _buildListActionButtonWhenHover() + else + _buildDateTimeForMobileTabletScreen() + ])), + Transform( + transform: Matrix4.translationValues(0.0, isHoverItem ? -10.0 : 0.0, 0.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: EdgeInsets.only(top: isHoverItem ? 0 : 8), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded(child: _searchStatus == SearchStatus.ACTIVE && _searchQuery != null && _searchQuery!.value.isNotEmpty + ? RichTextBuilder( + '${_presentationEmail.getEmailTitle()}', + '${_searchQuery!.value}', + TextStyle(fontSize: 13, color: AppColor.colorNameEmail), + TextStyle(fontSize: 13, backgroundColor: AppColor.bgWordSearch, color: AppColor.colorNameEmail)).build() + : Text( + '${_presentationEmail.getEmailTitle()}', + maxLines: 1, + style: TextStyle(fontSize: 13, color: AppColor.colorNameEmail)) + ), + if (_searchStatus == SearchStatus.ACTIVE && _presentationEmail.mailboxName.isNotEmpty) + Container( + margin: EdgeInsets.only(left: 8), + padding: EdgeInsets.only(left: 8, right: 8, top: 3, bottom: 3), + constraints: BoxConstraints(maxWidth: 100), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: AppColor.backgroundCounterMailboxColor), + child: Text( + '${_presentationEmail.mailboxName}', + maxLines: 1, + style: TextStyle(fontSize: 10, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold), + ) + ), + if (_presentationEmail.isFlaggedEmail() ) + (ButtonBuilder(_imagePaths.icStar) + ..paddingIcon(EdgeInsets.zero) + ..size(15)) + .build(), + ], + )), + Padding( + padding: EdgeInsets.only(top: 8), + child: Row(children: [ + Expanded(child: _searchStatus == + SearchStatus.ACTIVE && _searchQuery != null && + _searchQuery!.value.isNotEmpty + ? RichTextBuilder( + '${_presentationEmail.getPartialContent()}', + '${_searchQuery!.value}', + TextStyle(fontSize: 13, color: AppColor.colorContentEmail), + TextStyle(fontSize: 13, color: AppColor.colorContentEmail, backgroundColor: AppColor.bgWordSearch)).build() + : Text( + '${_presentationEmail.getPartialContent()}', + maxLines: 1, + style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail)) + ), + ])), + if (_selectModeAll == SelectMode.INACTIVE) + Padding( + padding: EdgeInsets.only(top: 12), + child: Divider(color: AppColor.lineItemListColor, height: 1, thickness: 0.2)), + ], + ) + ), + ])) + ]), + ); + }); + } + + Widget _buildListTileForDesktop() { + return StatefulBuilder(builder: (BuildContext context, StateSetter setState) { + return InkWell( + onTap: () => _emailActionClick?.call(EmailActionType.preview, _presentationEmail), + onLongPress: () => _emailActionClick?.call(EmailActionType.selection, _presentationEmail), + onHover: (value) => setState(() => isHoverItem = value), + child: Column(children: [ + Row(children: [ + Container( + padding: EdgeInsets.only(left: 16, right: 16), + alignment: Alignment.center, + child: _presentationEmail.isUnReadEmail() + ? SvgPicture.asset(_imagePaths.icUnreadStatus, width: 9, height: 9, fit: BoxFit.fill) + : SizedBox(width: 9)), + buildIconWeb( + icon: SvgPicture.asset( + _presentationEmail.isFlaggedEmail() ? _imagePaths.icStar : _imagePaths.icUnStar, + width: 20, + height: 20, + fit: BoxFit.fill), + tooltip: _presentationEmail.isFlaggedEmail() ? AppLocalizations.of(_context).starred : AppLocalizations.of(_context).not_starred, + onTap: () => _emailActionClick?.call( + _presentationEmail.isFlaggedEmail() ? EmailActionType.markAsUnStar : EmailActionType.markAsStar, + _presentationEmail)), + if (_selectModeAll == SelectMode.INACTIVE) SizedBox(width: 8), + GestureDetector( + onTap: () { + if (_selectModeAll == SelectMode.ACTIVE) { + _emailActionClick?.call(EmailActionType.selection, _presentationEmail); + } else { + _emailActionClick?.call(EmailActionType.preview, _presentationEmail); + } + }, + child: _buildAvatarIcon( + iconSize: 32, + textStyle: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Colors.white), + paddingIconSelect: EdgeInsets.all(5)), + ), + if (_selectModeAll == SelectMode.INACTIVE) SizedBox(width: 10), + Container( + width: 160, + child: _isSearchEnabled + ? RichTextBuilder( + _getInformationSender(), + '${_searchQuery!.value}', + TextStyle(fontSize: 15, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600), + TextStyle(fontSize: 15, color: AppColor.colorNameEmail, backgroundColor: AppColor.bgWordSearch, fontWeight: FontWeight.w600)).build() + : Text('${_getInformationSender()}', + maxLines: 1, + style: TextStyle(fontSize: 15, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600)), + ), + SizedBox(width: 24), + Expanded(child: _buildSubjectAndContent()), + SizedBox(width: 16), + if (isHoverItem) + _buildListActionButtonWhenHover() + else + _buildDateTimeForDesktopScreen() + ]), + if (_selectModeAll == SelectMode.INACTIVE) + Padding( + padding: EdgeInsets.only(top: 7.5, bottom: 7.5, left: 70), + child: Divider(color: AppColor.lineItemListColor, height: 1, thickness: 0.2)), + ]), + ); + }); + } + + Widget _buildListActionButtonWhenHover() { + return Row(children: [ + buildIconWeb( + icon: SvgPicture.asset( + _presentationEmail.isReadEmail() ? _imagePaths.icRead : _imagePaths.icUnread, + color: AppColor.colorActionButtonHover, + width: _responsiveUtils.isDesktop(_context) ? 16 : 14, + height: _responsiveUtils.isDesktop(_context) ? 16 : 14, + fit: BoxFit.fill), + splashRadius: _responsiveUtils.isDesktop(_context) ? 15 : 10, + tooltip: _presentationEmail.isReadEmail() ? AppLocalizations.of(_context).mark_as_unread : AppLocalizations.of(_context).mark_as_read, + onTap: () => _emailActionClick?.call( + _presentationEmail.isReadEmail() ? EmailActionType.markAsUnread : EmailActionType.markAsRead, + _presentationEmail)), + buildIconWeb( + icon: SvgPicture.asset( + _imagePaths.icMove, + width: _responsiveUtils.isDesktop(_context) ? 16 : 14, + height: _responsiveUtils.isDesktop(_context) ? 16 : 14, + color: AppColor.colorActionButtonHover, + fit: BoxFit.fill), + splashRadius: _responsiveUtils.isDesktop(_context) ? 15 : 10, + tooltip: AppLocalizations.of(_context).move, + onTap: () => _emailActionClick?.call(EmailActionType.move, _presentationEmail)), + buildIconWeb( + icon: SvgPicture.asset( + _imagePaths.icDelete, + width: _responsiveUtils.isDesktop(_context) ? 16 : 14, + height: _responsiveUtils.isDesktop(_context) ? 16 : 14, + color: AppColor.colorActionButtonHover, + fit: BoxFit.fill), + splashRadius: _responsiveUtils.isDesktop(_context) ? 15 : 10, + tooltip: _mailboxRole != PresentationMailbox.roleTrash + ? AppLocalizations.of(_context).move_to_trash + : AppLocalizations.of(_context).delete_permanently, + onTap: () => _emailActionClick?.call( + _mailboxRole != PresentationMailbox.roleTrash ? EmailActionType.moveToTrash : EmailActionType.deletePermanently, + _presentationEmail)), + if (_responsiveUtils.isDesktop(_context)) SizedBox(width: 16), + ]); + } + + Widget _buildDateTimeForDesktopScreen() { + return Row(children: [ + if (_searchStatus == SearchStatus.ACTIVE && _presentationEmail.mailboxName.isNotEmpty) + Container( + margin: EdgeInsets.only(left: 8), + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 3), + constraints: BoxConstraints(maxWidth: 100), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: AppColor.backgroundCounterMailboxColor), + child: Text('${_presentationEmail.mailboxName}', + maxLines: 1, + style: TextStyle(fontSize: 10, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold), + ) + ), + if (_presentationEmail.hasAttachment == true) + Padding(padding: EdgeInsets.only(left: 8), + child: (ButtonBuilder(_imagePaths.icAttachment) + ..paddingIcon(EdgeInsets.zero) + ..size(16)) + .build()), + Padding(padding: EdgeInsets.only(right: 20, left: 8), + child: Text('${_presentationEmail.getReceivedAt(Localizations.localeOf(_context).toLanguageTag())}', + maxLines: 1, + style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail, fontWeight: FontWeight.normal))) + ]); + } + + Widget _buildDateTimeForMobileTabletScreen() { + return Row(children: [ + if (_presentationEmail.hasAttachment == true) + Padding( + padding: EdgeInsets.only(left: 8), + child: (ButtonBuilder(_imagePaths.icAttachment) + ..paddingIcon(EdgeInsets.zero) + ..size(16)) + .build()), + Padding( + padding: EdgeInsets.only(right: 4, left: 8), + child: Text('${_presentationEmail.getReceivedAt(Localizations.localeOf(_context).toLanguageTag())}', + maxLines: 1, + style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail))), + (ButtonBuilder(_imagePaths.icChevron) + ..paddingIcon(EdgeInsets.zero) + ..size(16)) + .build() + ]); + } + + Widget _buildSubjectAndContent() { + return LayoutBuilder(builder: (context, constraints) { + return Row(children: [ + Container( + constraints: BoxConstraints(maxWidth: constraints.maxWidth / 2), + child: _isSearchEnabled + ? RichTextBuilder( + '${_presentationEmail.getEmailTitle()}', + '${_searchQuery!.value}', + TextStyle(fontSize: 13, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600), + TextStyle(fontSize: 13, backgroundColor: AppColor.bgWordSearch, color: AppColor.colorNameEmail)).build() + : Text('${_presentationEmail.getEmailTitle()}', + maxLines: 1, + style: TextStyle(fontSize: 13, color: AppColor.colorNameEmail, fontWeight: FontWeight.w600)) + ), + if (_presentationEmail.getEmailTitle().isNotEmpty) SizedBox(width: 12), + Expanded( + child: Container( + child: _isSearchEnabled + ? RichTextBuilder( + '${_presentationEmail.getPartialContent()}', + '${_searchQuery!.value}', + TextStyle(fontSize: 13, color: AppColor.colorContentEmail, fontWeight: FontWeight.normal), + TextStyle(fontSize: 13, color: AppColor.colorContentEmail, backgroundColor: AppColor.bgWordSearch)).build() + : Text('${_presentationEmail.getPartialContent()}', + maxLines: 1, + style: TextStyle(fontSize: 13, color: AppColor.colorContentEmail, fontWeight: FontWeight.normal)) + ) + ), + ]); + }); + } + + Widget _buildAvatarIcon({ + double? iconSize, + EdgeInsets? paddingIconSelect, + TextStyle? textStyle + }) { + if (_selectModeAll == SelectMode.ACTIVE) { + return Tooltip( + child: Padding( + padding: EdgeInsets.all(12), + child: SvgPicture.asset(_presentationEmail.selectMode == SelectMode.ACTIVE ? _imagePaths.icSelected : _imagePaths.icUnSelected, fit: BoxFit.fill)), + message: AppLocalizations.of(_context).select); + } else { + return (AvatarBuilder() + ..text('${_presentationEmail.getAvatarText()}') + ..size(iconSize ?? 48) + ..textColor(Colors.white) + ..addTextStyle(textStyle) + ..avatarColor(_presentationEmail.avatarColors)) + .build(); + } + } + + String _getInformationSender() { + if (_mailboxRole == PresentationMailbox.roleSent + || _mailboxRole == PresentationMailbox.roleDrafts + || _mailboxRole == PresentationMailbox.roleOutbox) { + return '${_presentationEmail.recipientsName()}'; + } + return '${_presentationEmail.getSenderName()}'; + } + + bool get _isSearchEnabled => _searchStatus == SearchStatus.ACTIVE + && _searchQuery != null + && _searchQuery!.value.isNotEmpty; +} \ No newline at end of file diff --git a/model/lib/email/email_action_type.dart b/model/lib/email/email_action_type.dart index adc6955e6..4c12f14b1 100644 --- a/model/lib/email/email_action_type.dart +++ b/model/lib/email/email_action_type.dart @@ -13,4 +13,6 @@ enum EmailActionType { composeFromEmailAddress, moveToTrash, deletePermanently, + preview, + selection, } \ No newline at end of file