diff --git a/lib/features/email/presentation/constants/email_contants.dart b/lib/features/email/presentation/constants/email_contants.dart new file mode 100644 index 0000000000..d3c8d15b14 --- /dev/null +++ b/lib/features/email/presentation/constants/email_contants.dart @@ -0,0 +1,5 @@ + +class EmailConstants { + static const HTML_TEXT = 'text/html'; + static const PLAIN_TEXT = 'text/plain'; +} \ No newline at end of file diff --git a/lib/features/email/presentation/email_bindings.dart b/lib/features/email/presentation/email_bindings.dart index a6a41bfe72..ad656f1155 100644 --- a/lib/features/email/presentation/email_bindings.dart +++ b/lib/features/email/presentation/email_bindings.dart @@ -1,10 +1,20 @@ -import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; +import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart'; +import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; +import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.dart'; +import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; import 'package:tmail_ui_user/features/email/presentation/email_controller.dart'; class EmailBindings extends Bindings { @override void dependencies() { - Get.lazyPut(() => EmailController(Get.find())); + Get.lazyPut(() => EmailDataSourceImpl(Get.find())); + Get.lazyPut(() => Get.find()); + Get.lazyPut(() => EmailRepositoryImpl(Get.find())); + Get.lazyPut(() => Get.find()); + Get.lazyPut(() => GetEmailContentInteractor(Get.find())); + Get.put(EmailController(Get.find())); } } \ No newline at end of file diff --git a/lib/features/email/presentation/email_controller.dart b/lib/features/email/presentation/email_controller.dart index 81050cd0dd..aca243caaa 100644 --- a/lib/features/email/presentation/email_controller.dart +++ b/lib/features/email/presentation/email_controller.dart @@ -1,22 +1,50 @@ import 'package:core/core.dart'; +import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart'; +import 'package:url_launcher/url_launcher.dart'; class EmailController extends BaseController { - final ResponsiveUtils responsiveUtils; + final mailboxDashBoardController = Get.find(); + final GetEmailContentInteractor _getEmailContentInteractor; - EmailController(this.responsiveUtils); + final emailAddressExpandMode = ExpandMode.COLLAPSE.obs; + + EmailController(this._getEmailContentInteractor); @override void onReady() { super.onReady(); + mailboxDashBoardController.selectedEmail.listen((presentationEmail) { + toggleDisplayEmailAddressAction(expandMode: ExpandMode.COLLAPSE); + final accountId = mailboxDashBoardController.accountId.value; + if (accountId != null && presentationEmail != null) { + _getEmailContentAction(accountId, presentationEmail.id); + } + }); } - void goToMailboxListMail(BuildContext context) { - Get.back(); + @override + void dispose() { + super.dispose(); + mailboxDashBoardController.selectedEmail.close(); + } + + void _getEmailContentAction(AccountId accountId, EmailId emailId) async { + consumeState(_getEmailContentInteractor.execute(accountId, emailId)); + } + + @override + void onData(Either newState) { + super.onData(newState); } @override @@ -26,4 +54,19 @@ class EmailController extends BaseController { @override void onError(error) { } + + void toggleDisplayEmailAddressAction({required ExpandMode expandMode}) { + emailAddressExpandMode.value = expandMode; + } + + void onClickLinkInEmailContentAction(String url) async { + final urlValid = '${Uri.decodeFull(url)}'; + if (await canLaunch(urlValid)) { + await launch(urlValid); + } + } + + void goToThreadView(BuildContext context) { + Get.back(); + } } \ No newline at end of file diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index 5ef21a672a..8a9238b80c 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -1,16 +1,21 @@ import 'package:core/core.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:model/model.dart'; +import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; import 'package:tmail_ui_user/features/email/presentation/email_controller.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/app_bar_mail_widget_builder.dart'; +import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_file_tile_builder.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/bottom_bar_mail_widget_builder.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/email/presentation/extensions/email_content_extension.dart'; +import 'package:tmail_ui_user/features/email/presentation/widgets/message_content_tile_builder.dart'; +import 'package:tmail_ui_user/features/email/presentation/widgets/sender_and_receiver_information_tile_builder.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -class EmailView extends GetWidget { +class EmailView extends GetWidget { - final mailboxDashBoardController = Get.find(); + final emailController = Get.find(); final responsiveUtils = Get.find(); final imagePaths = Get.find(); @@ -29,7 +34,7 @@ class EmailView extends GetWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ _buildAppBar(context), - Expanded(child: _buildListMail(context)), + Expanded(child: _buildEmailContent(context)), _buildBottomBar(context), ]) ) @@ -38,35 +43,157 @@ class EmailView extends GetWidget { } Widget _buildAppBar(BuildContext context) { - return Padding( - padding: EdgeInsets.only(left: 16, top: 16.0, right: 16, bottom: 16), - child: AppBarMailWidgetBuilder(context, imagePaths, responsiveUtils) - .onBackActionClick(() => controller.goToMailboxListMail(context)) - .build()); + return Obx(() => Padding( + padding: EdgeInsets.only(left: 6, top: 6, right: 6, bottom: 6), + child: AppBarMailWidgetBuilder( + context, + imagePaths, + responsiveUtils, + emailController.mailboxDashBoardController.selectedEmail.value) + .onBackActionClick(() => emailController.goToThreadView(context)) + .build())); } Widget _buildBottomBar(BuildContext context) { return Padding( - padding: EdgeInsets.only(left: 16, top: 16.0, right: 16, bottom: 16), + padding: EdgeInsets.only(left: 6, top: 6, right: 6, bottom: 6), child: BottomBarMailWidgetBuilder(context, imagePaths, responsiveUtils) .build()); } - Widget _buildListMail(BuildContext context) { + Widget _buildEmailContent(BuildContext context) { return Container( + color: AppColor.bgMessenger, margin: EdgeInsets.zero, - width: double.infinity, - height: double.infinity, + alignment: Alignment.topCenter, + child: Obx(() => emailController.mailboxDashBoardController.selectedEmail.value != null + ? SingleChildScrollView( + physics : ClampingScrollPhysics(), + child: Container( + margin: EdgeInsets.zero, + width: double.infinity, + alignment: Alignment.center, + padding: EdgeInsets.all(16), + color: AppColor.bgMessenger, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + _buildEmailSubject(), + _buildEmailMessage(context), + ]) + )) + : Center(child: _buildEmailEmpty(context)) + ) + ); + } + + Widget _buildEmailEmpty(BuildContext context) { + return Text( + AppLocalizations.of(context).no_mail_selected, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 25, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold)); + } + + Widget _buildEmailSubject() { + return Padding( + padding: EdgeInsets.only(left: 8, top: 4, bottom: 16), + child: Text( + '${emailController.mailboxDashBoardController.selectedEmail.value?.subject}', + style: TextStyle(fontSize: 18, color: AppColor.mailboxTextColor, fontWeight: FontWeight.w500) + )); + } + + Widget _buildLoadingView() { + return Obx(() => emailController.viewState.value.fold( + (failure) => SizedBox.shrink(), + (success) => success == UIState.loading + ? Center(child: Padding( + padding: EdgeInsets.only(top: 25, bottom: 16), + child: SizedBox( + width: 30, + height: 30, + child: CircularProgressIndicator(color: AppColor.primaryColor)))) + : SizedBox.shrink())); + } + + Widget _buildEmailMessage(BuildContext context) { + return Container( + padding: EdgeInsets.only(left: 12, right: 12, top: 16, bottom: 24), alignment: Alignment.center, - padding: EdgeInsets.all(20), - color: AppColor.bgMessenger, - child: Obx(() => Text( - mailboxDashBoardController.selectedEmail.value != PresentationEmail.presentationEmailEmpty - ? '${mailboxDashBoardController.selectedEmail.value.preview}' - : AppLocalizations.of(context).no_mail_selected, - textAlign: TextAlign.center, - style: TextStyle(fontSize: 25, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold) - )) + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: Colors.white), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Obx(() => SenderAndReceiverInformationTileBuilder( + context, + imagePaths, + emailController.mailboxDashBoardController.selectedEmail.value, + emailController.emailAddressExpandMode.value) + .onOpenExpandAddressReceiverActionClick(() => emailController.toggleDisplayEmailAddressAction(expandMode: ExpandMode.EXPAND)) + .build()), + _buildLoadingView(), + SizedBox(height: 16), + _buildListAttachments(context), + _buildListMessageContent(), + ], + ) + ); + } + + Widget _buildListAttachments(BuildContext context) { + return Obx(() => emailController.viewState.value.fold( + (failure) => SizedBox.shrink(), + (success) { + if (success is GetEmailContentSuccess && success.emailContent != null) { + final attachments = success.emailContent!.getListAttachment(); + return attachments.isNotEmpty + ? GridView.builder( + key: Key('list_attachment'), + primary: false, + shrinkWrap: true, + padding: EdgeInsets.only(bottom: 16), + itemCount: attachments.length, + gridDelegate: SliverGridDelegateFixedHeight( + height: 60, + crossAxisCount: responsiveUtils.isMobile(context) ? 2 : 4, + crossAxisSpacing: 16.0, + mainAxisSpacing: 8.0), + itemBuilder: (context, index) => AttachmentFileTileBuilder(imagePaths, attachments[index]).build()) + : SizedBox.shrink(); + } else { + return SizedBox.shrink(); + } + }) + ); + } + + Widget _buildListMessageContent() { + return Obx(() => emailController.viewState.value.fold( + (failure) => SizedBox.shrink(), + (success) { + if (success is GetEmailContentSuccess && success.emailContent != null) { + final messageContents = success.emailContent!.getListMessageContent(); + return messageContents.isNotEmpty + ? ListView.builder( + primary: false, + shrinkWrap: true, + key: Key('list_message_content'), + itemCount: messageContents.length, + itemBuilder: (context, index) => + MessageContentTileBuilder(messageContents[index], imagePaths) + .onOpenExternalUrlActionClick((url) => emailController.onClickLinkInEmailContentAction(url)) + .build()) + : SizedBox.shrink(); + } else { + return SizedBox.shrink(); + } + }) ); } } \ No newline at end of file diff --git a/lib/features/email/presentation/extensions/email_body_part_extension.dart b/lib/features/email/presentation/extensions/email_body_part_extension.dart new file mode 100644 index 0000000000..c7adcb4dd7 --- /dev/null +++ b/lib/features/email/presentation/extensions/email_body_part_extension.dart @@ -0,0 +1,6 @@ +import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/attachment_file.dart'; + +extension EmailBodyPartExtension on EmailBodyPart { + AttachmentFile toAttachmentFile() => AttachmentFile(partId, blobId, size, name, type); +} diff --git a/lib/features/email/presentation/extensions/email_content_extension.dart b/lib/features/email/presentation/extensions/email_content_extension.dart new file mode 100644 index 0000000000..aa1152e369 --- /dev/null +++ b/lib/features/email/presentation/extensions/email_content_extension.dart @@ -0,0 +1,39 @@ +import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart'; +import 'package:model/email/email_content.dart'; +import 'package:tmail_ui_user/features/email/presentation/constants/email_contants.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/attachment_file.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/message_content.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/text_format.dart'; +import 'package:tmail_ui_user/features/email/presentation/extensions/email_body_part_extension.dart'; + +extension EmailContentExtension on EmailContent { + + List getListMessageContent() { + Map mapHtmlBody = Map(); + List listMessageContent = []; + + htmlBody?.forEach((element) { + if (element.partId != null && element.type != null) { + mapHtmlBody[element.partId!] = element.type!.mimeType == EmailConstants.HTML_TEXT + ? TextFormat.HTML + : TextFormat.PLAIN; + } + }); + + bodyValues?.forEach((key, value) { + listMessageContent.add(MessageContent(mapHtmlBody[key] ?? TextFormat.PLAIN, value.value)); + }); + + return listMessageContent; + } + + List getListAttachment() { + if (attachments != null) { + return attachments! + .where((element) => element.disposition != 'inline') + .map((item) => item.toAttachmentFile()) + .toList(); + } + return []; + } +} \ No newline at end of file diff --git a/lib/features/email/presentation/extensions/presentation_email_extension.dart b/lib/features/email/presentation/extensions/presentation_email_extension.dart new file mode 100644 index 0000000000..3c22fab4c9 --- /dev/null +++ b/lib/features/email/presentation/extensions/presentation_email_extension.dart @@ -0,0 +1,9 @@ +import 'package:intl/intl.dart'; +import 'package:model/model.dart'; + +extension PresentationEmailExtension on PresentationEmail { + + int numberOfAllEmailAddress() => to.numberEmailAddress() + cc.numberEmailAddress() + bcc.numberEmailAddress(); + + String getSentTime() => sentAt != null ? DateFormat('dd/MM/yyyy h:mm a', 'en_US').format(sentAt!.value) : ''; +} \ No newline at end of file diff --git a/lib/features/email/presentation/model/attachment_file.dart b/lib/features/email/presentation/model/attachment_file.dart new file mode 100644 index 0000000000..10c5ee137a --- /dev/null +++ b/lib/features/email/presentation/model/attachment_file.dart @@ -0,0 +1,26 @@ + +import 'package:equatable/equatable.dart'; +import 'package:http_parser/http_parser.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart'; + +class AttachmentFile with EquatableMixin { + + final PartId? partId; + final Id? blobId; + final UnsignedInt? size; + final String? name; + final MediaType? type; + + AttachmentFile( + this.partId, + this.blobId, + this.size, + this.name, + this.type + ); + + @override + List get props => [partId, blobId, size, name, type]; +} \ No newline at end of file diff --git a/lib/features/email/presentation/model/message_content.dart b/lib/features/email/presentation/model/message_content.dart new file mode 100644 index 0000000000..304a087706 --- /dev/null +++ b/lib/features/email/presentation/model/message_content.dart @@ -0,0 +1,14 @@ + +import 'package:equatable/equatable.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/text_format.dart'; + +class MessageContent with EquatableMixin { + + final TextFormat textFormat; + final String content; + + MessageContent(this.textFormat, this.content); + + @override + List get props => [textFormat, content]; +} \ No newline at end of file diff --git a/lib/features/email/presentation/model/text_format.dart b/lib/features/email/presentation/model/text_format.dart new file mode 100644 index 0000000000..15e1f221cf --- /dev/null +++ b/lib/features/email/presentation/model/text_format.dart @@ -0,0 +1,5 @@ + +enum TextFormat { + HTML, + PLAIN +} \ No newline at end of file diff --git a/lib/features/email/presentation/widgets/app_bar_mail_widget_builder.dart b/lib/features/email/presentation/widgets/app_bar_mail_widget_builder.dart index c26464d025..dd4fcd5de7 100644 --- a/lib/features/email/presentation/widgets/app_bar_mail_widget_builder.dart +++ b/lib/features/email/presentation/widgets/app_bar_mail_widget_builder.dart @@ -2,6 +2,9 @@ import 'package:core/core.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:get/get.dart'; +import 'package:model/model.dart'; +import 'package:tmail_ui_user/main/routes/app_routes.dart'; typedef OnBackActionClick = void Function(); @@ -11,11 +14,13 @@ class AppBarMailWidgetBuilder { final BuildContext _context; final ImagePaths _imagePaths; final ResponsiveUtils _responsiveUtils; + final PresentationEmail? _presentationEmail; AppBarMailWidgetBuilder( this._context, this._imagePaths, this._responsiveUtils, + this._presentationEmail, ); AppBarMailWidgetBuilder onBackActionClick( @@ -28,6 +33,7 @@ class AppBarMailWidgetBuilder { return Container( key: Key('app_bar_messenger_widget'), alignment: Alignment.center, + padding: EdgeInsets.zero, color: Colors.white, child: MediaQuery( data: MediaQueryData(padding: EdgeInsets.zero), @@ -35,7 +41,7 @@ class AppBarMailWidgetBuilder { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, children: [ - if (!_responsiveUtils.isDesktop(_context)) + if (!_responsiveUtils.isDesktop(_context) || Get.currentRoute != AppRoutes.MAILBOX_DASHBOARD) Expanded(child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, @@ -63,11 +69,15 @@ class AppBarMailWidgetBuilder { crossAxisAlignment: CrossAxisAlignment.center, children: [ ButtonBuilder(_imagePaths.icTrash).key(Key('button_delete_message')).build(), - SizedBox(width: 20), + SizedBox(width: 10), ButtonBuilder(_imagePaths.icEyeDisable).key(Key('button_hide_message')).build(), - SizedBox(width: 20), - ButtonBuilder(_imagePaths.icStar).key(Key('button_favorite_message')).build(), - SizedBox(width: 20), + SizedBox(width: 10), + ButtonBuilder((_presentationEmail != null && _presentationEmail!.isFlaggedEmail()) + ? _imagePaths.icFlagged + : _imagePaths.icFlag) + .key(Key('button_favorite_message')) + .build(), + SizedBox(width: 10), ButtonBuilder(_imagePaths.icFolder).key(Key('button_add_folder_message')).build(), ] ); diff --git a/lib/features/email/presentation/widgets/attachment_file_tile_builder.dart b/lib/features/email/presentation/widgets/attachment_file_tile_builder.dart new file mode 100644 index 0000000000..2d560465b0 --- /dev/null +++ b/lib/features/email/presentation/widgets/attachment_file_tile_builder.dart @@ -0,0 +1,70 @@ + +import 'package:core/core.dart'; +import 'package:filesize/filesize.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/attachment_file.dart'; + +typedef OnOpenAttachmentFileActionClick = void Function(); + +class AttachmentFileTileBuilder { + + final ImagePaths _imagePaths; + final AttachmentFile _attachmentFile; + + OnOpenAttachmentFileActionClick? _onOpenAttachmentFileActionClick; + + AttachmentFileTileBuilder(this._imagePaths, this._attachmentFile); + + AttachmentFileTileBuilder onOpenAttachmentFileActionClick( + OnOpenAttachmentFileActionClick onOpenAttachmentFileActionClick) { + _onOpenAttachmentFileActionClick = onOpenAttachmentFileActionClick; + return this; + } + + Widget build() { + return Theme( + data: ThemeData( + splashColor: Colors.transparent , + highlightColor: Colors.transparent), + child: Container( + key: Key('attach_file_tile'), + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: AppColor.attachmentFileBorderColor), + color: Colors.white), + child: MediaQuery( + data: MediaQueryData(padding: EdgeInsets.zero), + child: ListTile( + onTap: () { + if (_onOpenAttachmentFileActionClick != null) { + _onOpenAttachmentFileActionClick!(); + }}, + leading: Transform( + transform: Matrix4.translationValues(0.0, 2.0, 0.0), + child: SvgPicture.asset(_imagePaths.icAttachmentFile, width: 24, height: 24, fit: BoxFit.fill)), + title: Transform( + transform: Matrix4.translationValues(-18.0, -8.0, 0.0), + child: Text( + _attachmentFile.name ?? '', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 12, color: AppColor.attachmentFileNameColor, fontWeight: FontWeight.w500), + )), + subtitle: _attachmentFile.size != null && _attachmentFile.size?.value != 0 + ? Transform( + transform: Matrix4.translationValues(-18.0, -8.0, 0.0), + child: Text( + filesize(_attachmentFile.size?.value), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 12, color: AppColor.attachmentFileSizeColor))) + : null + ), + ) + ) + ); + } +} \ No newline at end of file diff --git a/lib/features/email/presentation/widgets/message_content_tile_builder.dart b/lib/features/email/presentation/widgets/message_content_tile_builder.dart new file mode 100644 index 0000000000..a99f123e33 --- /dev/null +++ b/lib/features/email/presentation/widgets/message_content_tile_builder.dart @@ -0,0 +1,95 @@ + +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; +import 'package:photo_view/photo_view.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/message_content.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/text_format.dart'; + +typedef OnOpenExternalUrlActionClick = void Function(String url); + +class MessageContentTileBuilder { + + final MessageContent _messageContent; + final ImagePaths _imagePaths; + + OnOpenExternalUrlActionClick? _onOpenExternalUrlActionClick; + + MessageContentTileBuilder(this._messageContent, this._imagePaths); + + MessageContentTileBuilder onOpenExternalUrlActionClick(OnOpenExternalUrlActionClick onOpenExternalUrlActionClick) { + _onOpenExternalUrlActionClick = onOpenExternalUrlActionClick; + return this; + } + + Widget build() { + return Theme( + data: ThemeData( + splashColor: Colors.transparent, + highlightColor: Colors.transparent), + child: MediaQuery( + data: MediaQueryData(padding: EdgeInsets.zero), + child: Center( + child: _messageContent.textFormat == TextFormat.PLAIN + ? Text('${_messageContent.content}', style: TextStyle(fontSize: 12, color: AppColor.mailboxTextColor)) + : HtmlWidget( + '${_messageContent.content}', + textStyle: TextStyle(fontSize: 12, color: AppColor.mailboxTextColor), + buildAsync: true, + enableCaching: true, + factoryBuilder: () => _PopupPhotoViewWidgetFactory(_imagePaths), + buildAsyncBuilder: (_, snapshot) => snapshot.data ?? SizedBox( + height: 30, + width: 30, + child: CircularProgressIndicator(color: AppColor.primaryColor)), + onTapUrl: (url) { + if (_onOpenExternalUrlActionClick != null) { + _onOpenExternalUrlActionClick!(url); + } + }, + webView: true + ), + ) + ) + ); + } +} + +class _PopupPhotoViewWidgetFactory extends WidgetFactory { + + final ImagePaths _imagePaths; + + _PopupPhotoViewWidgetFactory(this._imagePaths); + + @override + Widget? buildImageWidget(BuildMetadata meta, {String? semanticLabel, required String url}) { + final built = super.buildImageWidget(meta, semanticLabel: semanticLabel, url: url); + + if (built is Image) { + return Builder( + builder: (context) => GestureDetector( + onTap: () => Navigator.of(context).push(MaterialPageRoute( + builder: (_) => Scaffold( + body: Stack( + children: [ + PhotoView( + heroAttributes: PhotoViewHeroAttributes(tag: url), + imageProvider: built.image), + Padding( + padding: EdgeInsets.only(left: 16, top: 35), + child: IconButton( + onPressed: () => Navigator.of(context).pop(), + icon: SvgPicture.asset(_imagePaths.icCloseMailbox, width: 30, height: 30, fit: BoxFit.fill))) + ], + ), + ))), + child: Hero(tag: url, child: built), + ), + ); + } + + return built; + } +} \ No newline at end of file diff --git a/lib/features/email/presentation/widgets/sender_and_receiver_information_tile_builder.dart b/lib/features/email/presentation/widgets/sender_and_receiver_information_tile_builder.dart new file mode 100644 index 0000000000..d25b988086 --- /dev/null +++ b/lib/features/email/presentation/widgets/sender_and_receiver_information_tile_builder.dart @@ -0,0 +1,196 @@ + +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:model/model.dart'; +import 'package:tmail_ui_user/features/email/presentation/extensions/presentation_email_extension.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +typedef OnOpenExpandAddressReceiverActionClick = void Function(); + +class SenderAndReceiverInformationTileBuilder { + + static const int LIMIT_ADDRESS_DISPLAY = 1; + + final ImagePaths _imagePaths; + final BuildContext _context; + final PresentationEmail? _presentationEmail; + final ExpandMode _expandMode; + + OnOpenExpandAddressReceiverActionClick? _onOpenExpandAddressReceiverActionClick; + + SenderAndReceiverInformationTileBuilder( + this._context, + this._imagePaths, + this._presentationEmail, + this._expandMode, + ); + + SenderAndReceiverInformationTileBuilder onOpenExpandAddressReceiverActionClick(OnOpenExpandAddressReceiverActionClick onOpenExpandAddressReceiverActionClick) { + _onOpenExpandAddressReceiverActionClick = onOpenExpandAddressReceiverActionClick; + return this; + } + + Widget build() { + if (_presentationEmail == null) { + return SizedBox.shrink(); + } + return Theme( + data: ThemeData( + splashColor: Colors.transparent, + highlightColor: Colors.transparent), + child: MediaQuery( + data: MediaQueryData(padding: EdgeInsets.zero), + child: ListTile( + leading: Transform( + transform: Matrix4.translationValues(-15.0, -8.0, 0.0), + child: AvatarBuilder() + .text('${_presentationEmail!.getAvatarText()}') + .size(40) + .iconStatus(_imagePaths.icOffline) + .build()), + title: Transform( + transform: Matrix4.translationValues(-15.0, 0.0, 0.0), + child: Text( + '${AppLocalizations.of(_context).from_email_address_prefix}: ${_presentationEmail!.getSenderName().inCaps}', + style: TextStyle(fontSize: 16, color: AppColor.nameUserColor, fontWeight: FontWeight.w500), + )), + subtitle: Transform( + transform: Matrix4.translationValues(-15.0, 0.0, 0.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (_presentationEmail!.to.numberEmailAddress() > 0) _buildAddressToReceiverWidget(), + if (_presentationEmail!.cc.numberEmailAddress() > 0) _buildAddressCcReceiverWidget(), + if (_presentationEmail!.bcc.numberEmailAddress() > 0) _buildAddressBccReceiverWidget(), + Padding( + padding: EdgeInsets.only(top: 6), + child: Text( + '${_presentationEmail!.getSentTime()}', + style: TextStyle(fontSize: 12, color: AppColor.baseTextColor), + )) + ], + )) + ) + ) + ); + } + + Widget _buildExpandAddressWidget(String typeReceiver, String nameAddress) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '$typeReceiver: ', + style: TextStyle(fontSize: 12, color: AppColor.baseTextColor, fontWeight: FontWeight.w500)), + Expanded(child: Text( + '$nameAddress', + style: TextStyle(fontSize: 12, color: AppColor.baseTextColor, fontWeight: FontWeight.w500), + )) + ]); + } + + Widget _buildCollapseAddressWidget(String typeReceiver, String nameAddress) { + return Row( + children: [ + Text( + '$typeReceiver: $nameAddress', + style: TextStyle(fontSize: 12, color: AppColor.baseTextColor, fontWeight: FontWeight.w500)), + if (_expandMode == ExpandMode.COLLAPSE + && _presentationEmail != null + && _presentationEmail!.numberOfAllEmailAddress() > LIMIT_ADDRESS_DISPLAY) + _buildButtonExpandAddress() + ]); + } + + Widget _buildAddressWidget(String typeReceiver, String nameAddress) { + if (_expandMode == ExpandMode.EXPAND + || (_presentationEmail != null && _presentationEmail!.numberOfAllEmailAddress() <= LIMIT_ADDRESS_DISPLAY)) { + return Padding( + padding: EdgeInsets.only(top: 6), + child: _buildExpandAddressWidget(typeReceiver, nameAddress)); + } else { + return Padding( + padding: EdgeInsets.only(top: 6), + child: _buildCollapseAddressWidget(typeReceiver, nameAddress)); + } + } + + Widget _buildAddressToReceiverWidget() { + return _buildAddressWidget( + AppLocalizations.of(_context).to_email_address_prefix, + _presentationEmail!.to.listEmailAddressToString(expandMode: _expandMode, limitAddress: LIMIT_ADDRESS_DISPLAY)); + } + + Widget _buildAddressCcReceiverWidget() { + if (_presentationEmail!.to.numberEmailAddress() > 0) { + if (_expandMode == ExpandMode.EXPAND) { + return Padding( + padding: EdgeInsets.only(top: 6), + child: _buildExpandAddressWidget(AppLocalizations.of(_context).cc_email_address_prefix, _presentationEmail!.cc.listEmailAddressToString(expandMode: _expandMode, limitAddress: LIMIT_ADDRESS_DISPLAY))); + } else { + return SizedBox.shrink(); + } + } else { + return _buildAddressWidget( + AppLocalizations.of(_context).cc_email_address_prefix, + _presentationEmail!.cc.listEmailAddressToString(expandMode: _expandMode, limitAddress: LIMIT_ADDRESS_DISPLAY)); + } + } + + Widget _buildAddressBccReceiverWidget() { + if (_presentationEmail!.to.numberEmailAddress() > 0) { + if (_expandMode == ExpandMode.EXPAND) { + return Padding( + padding: EdgeInsets.only(top: 6), + child: _buildExpandAddressWidget(AppLocalizations.of(_context).bcc_email_address_prefix, _presentationEmail!.bcc.listEmailAddressToString(expandMode: _expandMode, limitAddress: LIMIT_ADDRESS_DISPLAY))); + } else { + return SizedBox.shrink(); + } + } else { + if (_presentationEmail!.cc.numberEmailAddress() > 0) { + if (_expandMode == ExpandMode.EXPAND) { + return Padding( + padding: EdgeInsets.only(top: 6), + child: _buildExpandAddressWidget(AppLocalizations.of(_context).bcc_email_address_prefix, _presentationEmail!.bcc.listEmailAddressToString(expandMode: _expandMode, limitAddress: LIMIT_ADDRESS_DISPLAY))); + } else { + return SizedBox.shrink(); + } + } else { + return _buildAddressWidget( + AppLocalizations.of(_context).bcc_email_address_prefix, + _presentationEmail!.bcc.listEmailAddressToString(expandMode: _expandMode, limitAddress: LIMIT_ADDRESS_DISPLAY)); + } + } + } + + String getRemainCountAddressReceiver() { + if (_presentationEmail!.numberOfAllEmailAddress() - LIMIT_ADDRESS_DISPLAY >= 999) { + return '999'; + } + return '${_presentationEmail!.numberOfAllEmailAddress() - LIMIT_ADDRESS_DISPLAY}'; + } + + Widget _buildButtonExpandAddress() { + return GestureDetector( + onTap: () { + if (_onOpenExpandAddressReceiverActionClick != null) { + _onOpenExpandAddressReceiverActionClick!(); + }}, + child: Row( + children: [ + SizedBox(width: 8), + Text( + '+${getRemainCountAddressReceiver()}', + style: TextStyle(fontSize: 12, color: AppColor.baseTextColor, fontWeight: FontWeight.w500), + ), + SvgPicture.asset(_imagePaths.icMoreReceiver, width: 14, height: 14, fit: BoxFit.fill), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index 344f95d76c..1c13233d9b 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -98,8 +98,8 @@ class MailboxController extends BaseController { } catch (e) {} } - SelectMode getSelectMode(PresentationMailbox presentationMailbox, PresentationMailbox selectedMailbox) { - return presentationMailbox.id == selectedMailbox.id + SelectMode getSelectMode(PresentationMailbox presentationMailbox, PresentationMailbox? selectedMailbox) { + return presentationMailbox.id == selectedMailbox?.id ? SelectMode.ACTIVE : SelectMode.INACTIVE; } diff --git a/lib/features/mailbox/presentation/mailbox_view.dart b/lib/features/mailbox/presentation/mailbox_view.dart index 343969c41f..06bea3e5c1 100644 --- a/lib/features/mailbox/presentation/mailbox_view.dart +++ b/lib/features/mailbox/presentation/mailbox_view.dart @@ -86,7 +86,7 @@ class MailboxView extends GetWidget { Widget _buildCloseScreenButton(BuildContext context) { return Padding( - padding: EdgeInsets.only(left: 24, top: responsiveUtils.isMobile(context) ? 0 : 12), + padding: EdgeInsets.only(left: 16, top: responsiveUtils.isMobile(context) ? 0 : 12), child: IconButton( key: Key('mailbox_close_button'), onPressed: () => controller.closeMailboxScreen(), diff --git a/lib/features/mailbox/presentation/widgets/storage_widget_builder.dart b/lib/features/mailbox/presentation/widgets/storage_widget_builder.dart index 9c5ffdf94b..bc69a3aa29 100644 --- a/lib/features/mailbox/presentation/widgets/storage_widget_builder.dart +++ b/lib/features/mailbox/presentation/widgets/storage_widget_builder.dart @@ -17,7 +17,7 @@ class StorageWidgetBuilder { padding: EdgeInsets.only(left: 40, top: 16, bottom: 20, right: 40), color: AppColor.storageBackgroundColor, alignment: Alignment.bottomLeft, - height: 112, + height: 100, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/features/mailbox/presentation/widgets/user_information_widget_builder.dart b/lib/features/mailbox/presentation/widgets/user_information_widget_builder.dart index 61039023fe..70fdfd80f2 100644 --- a/lib/features/mailbox/presentation/widgets/user_information_widget_builder.dart +++ b/lib/features/mailbox/presentation/widgets/user_information_widget_builder.dart @@ -33,7 +33,10 @@ class UserInformationWidgetBuilder { _onOpenUserInformationActionClick!(); } }, - leading: SvgPicture.asset(_imagePaths.icTMailLogo, width: 40, height: 40, fit: BoxFit.fill), + leading: AvatarBuilder() + .text('J') + .size(40) + .build(), title: Transform( transform: Matrix4.translationValues(0.0, 0.0, 0.0), child: Text( diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart index f762d28984..8c2c925af5 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart @@ -1,4 +1,5 @@ import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/email/presentation/email_bindings.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_bindings.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_bindings.dart'; @@ -11,5 +12,6 @@ class MailboxDashBoardBindings extends Bindings { MailboxBindings().dependencies(); ThreadBindings().dependencies(); + EmailBindings().dependencies(); } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart index 0b370d23aa..f8eb6ffbb0 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart @@ -5,16 +5,15 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/email/presentation/email_controller.dart'; class MailboxDashBoardController extends BaseController { final scaffoldKey = GlobalKey(); - final selectedMailbox = PresentationMailbox.presentationMailboxEmpty.obs; - final selectedEmail = PresentationEmail.presentationEmailEmpty.obs; + final selectedMailbox = Rxn(); + final selectedEmail = Rxn(); final accountId = Rxn(); - Session? sessionCurrent; - MailboxDashBoardController(); @override @@ -33,24 +32,33 @@ class MailboxDashBoardController extends BaseController { void _setSessionCurrent() { Future.delayed(const Duration(milliseconds: 500), () { - sessionCurrent = Get.arguments as Session; - accountId.value = sessionCurrent?.accounts.keys.first; + final arguments = Get.arguments; + if (arguments is Session) { + final sessionCurrent = Get.arguments as Session; + accountId.value = sessionCurrent.accounts.keys.first; + } }); } - void setSelectedMailbox(PresentationMailbox newPresentationMailbox) { + void setSelectedMailbox(PresentationMailbox? newPresentationMailbox) { selectedMailbox.value = newPresentationMailbox; } - void setSelectedEmail(PresentationEmail newPresentationEmail) { + void setSelectedEmail(PresentationEmail? newPresentationEmail) { selectedEmail.value = newPresentationEmail; } void openDrawer() { - scaffoldKey.currentState?.openEndDrawer(); + scaffoldKey.currentState?.openDrawer(); } void closeDrawer() { scaffoldKey.currentState?.openEndDrawer(); } + + @override + void onClose() { + super.onClose(); + Get.delete(); + } } \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 68a68eda6e..463d8ff396 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -46,7 +46,7 @@ class ThreadController extends BaseController { void onReady() { super.onReady(); mailboxDashBoardController.selectedMailbox.listen((selectedMailbox) { - mailboxDashBoardController.setSelectedEmail(PresentationEmail.presentationEmailEmpty); + mailboxDashBoardController.setSelectedEmail(null); refreshGetAllEmailAction(); }); } @@ -80,17 +80,11 @@ class ThreadController extends BaseController { lastGetTotal = emailList.length; - if (newListEmail.isEmpty) { - _resetPositionCurrentAndLoadMoreState(); - } else { - loadMoreState.value = LoadMoreState.IDLE; - } + loadMoreState.value = newListEmail.isEmpty ? LoadMoreState.COMPLETED : LoadMoreState.IDLE; } EmailFilterCondition? _getFilterConditionCurrent() { - return mailboxDashBoardController.selectedMailbox.value.id != PresentationMailbox.presentationMailboxEmpty.id - ? EmailFilterCondition(inMailbox: mailboxDashBoardController.selectedMailbox.value.id) - : null; + return EmailFilterCondition(inMailbox: mailboxDashBoardController.selectedMailbox.value?.id); } Set? _getSortCurrent() { @@ -103,7 +97,7 @@ class ThreadController extends BaseController { if (loadMoreState.value == LoadMoreState.LOADING) { positionCurrent -= lastGetTotal; } - loadMoreState.value = LoadMoreState.COMPLETED; + loadMoreState.value = LoadMoreState.IDLE; } void getAllEmailAction(AccountId accountId, @@ -162,8 +156,8 @@ class ThreadController extends BaseController { } } - SelectMode getSelectMode(PresentationEmail presentationEmail, PresentationEmail selectedEmail) { - return presentationEmail.id == selectedEmail.id + SelectMode getSelectMode(PresentationEmail presentationEmail, PresentationEmail? selectedEmail) { + return presentationEmail.id == selectedEmail?.id ? SelectMode.ACTIVE : SelectMode.INACTIVE; } @@ -175,7 +169,7 @@ class ThreadController extends BaseController { } } - void goToMailbox() { + void openMailboxLeftMenu() { mailboxDashBoardController.openDrawer(); } diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 62416cf132..5d795f9514 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -25,6 +25,7 @@ class ThreadView extends GetWidget { left: false, child: Container( alignment: Alignment.center, + padding: EdgeInsets.zero, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -40,22 +41,22 @@ class ThreadView extends GetWidget { ); } - Widget _buildAppBarMailboxListMail(BuildContext context, PresentationMailbox presentationMailbox) { + Widget _buildAppBarMailboxListMail(BuildContext context, PresentationMailbox? presentationMailbox) { return Padding( - padding: EdgeInsets.only(left: 16, top: 8.0, right: 16), + padding: EdgeInsets.only(left: 12, right: 12), child: AppBarThreadWidgetBuilder( context, imagePaths, responsiveUtils, presentationMailbox) - .onOpenListMailboxActionClick(() => controller.goToMailbox()) + .onOpenListMailboxActionClick(() => controller.openMailboxLeftMenu()) .build()); } Widget _buildLoadingView() { return Obx(() => controller.viewState.value.fold( (failure) => SizedBox.shrink(), - (success) => success == UIState.loading + (success) => success == UIState.loading && controller.loadMoreState.value != LoadMoreState.LOADING ? Center(child: Padding( padding: EdgeInsets.only(top: 16, bottom: 16), child: SizedBox( @@ -107,7 +108,7 @@ class ThreadView extends GetWidget { return false; }, child: ListView.builder( - padding: EdgeInsets.only(top: 16, left: 16, right: 16), + padding: EdgeInsets.only(top: 16), key: Key('presentation_email_list'), itemCount: listPresentationEmail.length, itemBuilder: (context, index) => diff --git a/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart b/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart index 9c8bc50061..b521cf4b0b 100644 --- a/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart +++ b/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; typedef OnOpenSearchMailActionClick = void Function(); typedef OnOpenListMailboxActionClick = void Function(); @@ -18,7 +19,7 @@ class AppBarThreadWidgetBuilder { final BuildContext _context; final ImagePaths _imagePaths; final ResponsiveUtils _responsiveUtils; - final PresentationMailbox _presentationMailbox; + final PresentationMailbox? _presentationMailbox; AppBarThreadWidgetBuilder( this._context, @@ -49,6 +50,7 @@ class AppBarThreadWidgetBuilder { return Container( key: Key('app_bar_thread_widget'), alignment: Alignment.center, + padding: EdgeInsets.only(top: 8, bottom: 2), color: Colors.white, child: MediaQuery( data: MediaQueryData(padding: EdgeInsets.zero), @@ -72,13 +74,16 @@ class AppBarThreadWidgetBuilder { _onOpenUserInformationActionClick!() }}, child: Padding( - padding: EdgeInsets.only(left: _responsiveUtils.isMobile(_context) ? 24 : 0), - child: SvgPicture.asset(_imagePaths.icTMailLogo, width: 24, height: 24, fit: BoxFit.fill))); + padding: EdgeInsets.zero, + child: AvatarBuilder() + .text('J') + .size(36) + .build())); } Widget _buildIconSearch() { return Padding( - padding: EdgeInsets.only(left: 16, right: 0), + padding: EdgeInsets.only(left: 16), child: IconButton( icon: SvgPicture.asset(_imagePaths.icSearch, width: 24, height: 24, fit: BoxFit.fill), onPressed: () => { @@ -102,20 +107,20 @@ class AppBarThreadWidgetBuilder { child: Padding( padding: EdgeInsets.only(left: 16), child: Text( - '${_presentationMailbox.name?.name}', + '${_presentationMailbox?.name != null ? _presentationMailbox?.name?.name : ''}', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 22, color: AppColor.titleAppBarMailboxListMail, fontWeight: FontWeight.w500), ))), - if(_presentationMailbox.getCountUnReadEmails().isNotEmpty) + if(_presentationMailbox != null && _presentationMailbox!.getCountUnReadEmails().isNotEmpty) Container( margin: EdgeInsets.only(left: 9), - padding: EdgeInsets.only(left: 8, right: 8, top: 2, bottom: 2), + padding: EdgeInsets.only(left: 8, right: 8, top: 2.5, bottom: 2.5), decoration:BoxDecoration( borderRadius: BorderRadius.circular(8), color: AppColor.bgNotifyCountAppBarMailboxListMail), child: Text( - '${_presentationMailbox.getCountUnReadEmails()} new', + '${_presentationMailbox!.getCountUnReadEmails()} ${AppLocalizations.of(_context).unread_email_notification}', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 10, color: AppColor.notifyCountAppBarMailboxListMail, fontWeight: FontWeight.w500), diff --git a/lib/features/thread/presentation/widgets/email_tile_builder.dart b/lib/features/thread/presentation/widgets/email_tile_builder.dart index cdf55ac807..2ef37ee5ca 100644 --- a/lib/features/thread/presentation/widgets/email_tile_builder.dart +++ b/lib/features/thread/presentation/widgets/email_tile_builder.dart @@ -3,8 +3,8 @@ import 'dart:ui'; import 'package:core/core.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:model/model.dart'; +import 'package:tmail_ui_user/features/email/domain/extensions/presentation_email_extension.dart'; typedef OnOpenMailActionClick = void Function(); @@ -39,7 +39,7 @@ class EmailTileBuilder { child: Container( key: Key('thread_tile'), margin: EdgeInsets.only(bottom: 10), - padding: EdgeInsets.only(left: 16, right: 16, top: 10, bottom: 10), + padding: EdgeInsets.only(top: 10, bottom: 10, left: 20, right: 6), alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), @@ -51,8 +51,6 @@ class EmailTileBuilder { child: MediaQuery( data: MediaQueryData(padding: EdgeInsets.zero), child: ListTile( - focusColor: AppColor.primaryLightColor, - hoverColor: AppColor.primaryLightColor, contentPadding: EdgeInsets.zero, onTap: () => { if (_onOpenMailActionClick != null) { @@ -60,46 +58,39 @@ class EmailTileBuilder { } }, leading: Transform( - transform: Matrix4.translationValues(0.0, -12.0, 0.0), - child: SvgPicture.asset( - _imagePaths.icTMailLogo, - width: 32, - height: 32, - color: _selectMode == SelectMode.ACTIVE - ? _responsiveUtils.isDesktop(_context) ? AppColor.mailboxSelectedIconColor : AppColor.mailboxIconColor - : AppColor.mailboxIconColor, - fit: BoxFit.fill)), - title: Padding( - padding: EdgeInsets.only(left: 0), + transform: Matrix4.translationValues(-10.0, -10.0, 0.0), + child: AvatarBuilder() + .text('${_presentationEmail.getAvatarText()}') + .size(40) + .iconStatus(_imagePaths.icOffline) + .build()), + title: Transform( + transform: Matrix4.translationValues(-10.0, 0.0, 0.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Expanded( child: Text( - '${_presentationEmail.getSenderName()}', + '${_presentationEmail.getSenderName().inCaps}', maxLines: 1, overflow:TextOverflow.ellipsis, style: TextStyle( fontSize: 14, - color: _selectMode == SelectMode.ACTIVE - ? _responsiveUtils.isDesktop(_context) ? AppColor.mailboxSelectedTextColor : AppColor.mailboxTextColor - : AppColor.mailboxTextColor, - fontWeight: FontWeight.bold))), + color: AppColor.mailboxTextColor, + fontWeight: _presentationEmail.isUnReadEmail() ? FontWeight.bold : FontWeight.w500))), Text( - '${_presentationEmail.getTimeThisYear()}', + '${_presentationEmail.getTimeSentEmail()}', maxLines: 1, overflow:TextOverflow.ellipsis, style: TextStyle( - fontSize: 12, - color: _selectMode == SelectMode.ACTIVE - ? _responsiveUtils.isDesktop(_context) ? AppColor.mailboxSelectedTextColor : AppColor.mailboxTextColor - : AppColor.mailboxTextColor)) + fontSize: 12, + color: _presentationEmail.isUnReadEmail() ? AppColor.sentTimeTextColorUnRead : AppColor.baseTextColor)) ], ) ), - subtitle: Padding( - padding: EdgeInsets.only(left: 0, top: 8), + subtitle: Transform( + transform: Matrix4.translationValues(-10.0, 8.0, 0.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -112,8 +103,8 @@ class EmailTileBuilder { overflow:TextOverflow.ellipsis, style: TextStyle( fontSize: 12, - color: AppColor.baseTextColor, - fontWeight: FontWeight.bold))), + color: _presentationEmail.isUnReadEmail() ? AppColor.subjectEmailTextColorUnRead : AppColor.baseTextColor, + fontWeight: _presentationEmail.isUnReadEmail() ? FontWeight.bold : FontWeight.w500))), Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, @@ -124,9 +115,11 @@ class EmailTileBuilder { maxLines: 1, overflow:TextOverflow.ellipsis, style: TextStyle(fontSize: 12, color: AppColor.baseTextColor))), - if (_presentationEmail.hasAttachment == true) ButtonBuilder(_imagePaths.icShare).build(), - SizedBox(width: 12), - ButtonBuilder(_imagePaths.icStar).build(), + if (_presentationEmail.hasAttachment == true) ButtonBuilder(_imagePaths.icShare).padding(2).build(), + SizedBox(width: 2), + ButtonBuilder(_presentationEmail.isFlaggedEmail() ? _imagePaths.icFlagged : _imagePaths.icFlag) + .padding(2) + .build(), ], ) ], diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 1c996fae4e..ad0b6dcde7 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -59,5 +59,55 @@ "@new_folder": { "type": "text", "placeholders": {} + }, + "reply_all": "Reply all", + "@reply_all": { + "type": "text", + "placeholders": {} + }, + "reply": "Reply", + "@reply": { + "type": "text", + "placeholders": {} + }, + "forward": "Forward", + "@forward": { + "type": "text", + "placeholders": {} + }, + "no_emails": "No emails", + "@no_emails": { + "type": "text", + "placeholders": {} + }, + "no_mail_selected": "No email selected", + "@no_mail_selected": { + "type": "text", + "placeholders": {} + }, + "from_email_address_prefix": "From", + "@from_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "to_email_address_prefix": "To", + "@to_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "unread_email_notification": "new", + "@unread_email_notification": { + "type": "text", + "placeholders": {} + }, + "bcc_email_address_prefix": "Bcc", + "@bcc_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "cc_email_address_prefix": "Cc", + "@cc_email_address_prefix": { + "type": "text", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 1c996fae4e..ad0b6dcde7 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -59,5 +59,55 @@ "@new_folder": { "type": "text", "placeholders": {} + }, + "reply_all": "Reply all", + "@reply_all": { + "type": "text", + "placeholders": {} + }, + "reply": "Reply", + "@reply": { + "type": "text", + "placeholders": {} + }, + "forward": "Forward", + "@forward": { + "type": "text", + "placeholders": {} + }, + "no_emails": "No emails", + "@no_emails": { + "type": "text", + "placeholders": {} + }, + "no_mail_selected": "No email selected", + "@no_mail_selected": { + "type": "text", + "placeholders": {} + }, + "from_email_address_prefix": "From", + "@from_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "to_email_address_prefix": "To", + "@to_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "unread_email_notification": "new", + "@unread_email_notification": { + "type": "text", + "placeholders": {} + }, + "bcc_email_address_prefix": "Bcc", + "@bcc_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "cc_email_address_prefix": "Cc", + "@cc_email_address_prefix": { + "type": "text", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index f3ae662bca..fe2f98e17f 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2021-07-27T15:14:36.105457", + "@@last_modified": "2021-08-18T17:44:10.952718", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -59,5 +59,55 @@ "@new_folder": { "type": "text", "placeholders": {} + }, + "reply_all": "Reply all", + "@reply_all": { + "type": "text", + "placeholders": {} + }, + "reply": "Reply", + "@reply": { + "type": "text", + "placeholders": {} + }, + "forward": "Forward", + "@forward": { + "type": "text", + "placeholders": {} + }, + "no_emails": "No emails", + "@no_emails": { + "type": "text", + "placeholders": {} + }, + "no_mail_selected": "No email selected", + "@no_mail_selected": { + "type": "text", + "placeholders": {} + }, + "from_email_address_prefix": "From", + "@from_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "to_email_address_prefix": "To", + "@to_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "unread_email_notification": "new", + "@unread_email_notification": { + "type": "text", + "placeholders": {} + }, + "bcc_email_address_prefix": "Bcc", + "@bcc_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "cc_email_address_prefix": "Cc", + "@cc_email_address_prefix": { + "type": "text", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 1c996fae4e..ad0b6dcde7 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -59,5 +59,55 @@ "@new_folder": { "type": "text", "placeholders": {} + }, + "reply_all": "Reply all", + "@reply_all": { + "type": "text", + "placeholders": {} + }, + "reply": "Reply", + "@reply": { + "type": "text", + "placeholders": {} + }, + "forward": "Forward", + "@forward": { + "type": "text", + "placeholders": {} + }, + "no_emails": "No emails", + "@no_emails": { + "type": "text", + "placeholders": {} + }, + "no_mail_selected": "No email selected", + "@no_mail_selected": { + "type": "text", + "placeholders": {} + }, + "from_email_address_prefix": "From", + "@from_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "to_email_address_prefix": "To", + "@to_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "unread_email_notification": "new", + "@unread_email_notification": { + "type": "text", + "placeholders": {} + }, + "bcc_email_address_prefix": "Bcc", + "@bcc_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "cc_email_address_prefix": "Cc", + "@cc_email_address_prefix": { + "type": "text", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_vi.arb b/lib/l10n/intl_vi.arb index 1c996fae4e..ad0b6dcde7 100644 --- a/lib/l10n/intl_vi.arb +++ b/lib/l10n/intl_vi.arb @@ -59,5 +59,55 @@ "@new_folder": { "type": "text", "placeholders": {} + }, + "reply_all": "Reply all", + "@reply_all": { + "type": "text", + "placeholders": {} + }, + "reply": "Reply", + "@reply": { + "type": "text", + "placeholders": {} + }, + "forward": "Forward", + "@forward": { + "type": "text", + "placeholders": {} + }, + "no_emails": "No emails", + "@no_emails": { + "type": "text", + "placeholders": {} + }, + "no_mail_selected": "No email selected", + "@no_mail_selected": { + "type": "text", + "placeholders": {} + }, + "from_email_address_prefix": "From", + "@from_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "to_email_address_prefix": "To", + "@to_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "unread_email_notification": "new", + "@unread_email_notification": { + "type": "text", + "placeholders": {} + }, + "bcc_email_address_prefix": "Bcc", + "@bcc_email_address_prefix": { + "type": "text", + "placeholders": {} + }, + "cc_email_address_prefix": "Cc", + "@cc_email_address_prefix": { + "type": "text", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/bindings/network/network_bindings.dart b/lib/main/bindings/network/network_bindings.dart index bf80f0a3be..397c32510b 100644 --- a/lib/main/bindings/network/network_bindings.dart +++ b/lib/main/bindings/network/network_bindings.dart @@ -5,6 +5,7 @@ import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/http/http_client.dart' as JmapHttpClient; +import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/login/data/network/login_api.dart'; import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart'; import 'package:tmail_ui_user/features/session/data/network/session_api.dart'; @@ -49,5 +50,6 @@ class NetworkBindings extends Bindings { Get.put(MailboxAPI(Get.find())); Get.put(SessionAPI(Get.find())); Get.put(ThreadAPI(Get.find())); + Get.put(EmailAPI(Get.find())); } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index b4f76b5700..9023e51bec 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -121,5 +121,40 @@ class AppLocalizations { name: 'no_mail_selected', ); } + + String get from_email_address_prefix { + return Intl.message( + 'From', + name: 'from_email_address_prefix', + ); + } + + String get to_email_address_prefix { + return Intl.message( + 'To', + name: 'to_email_address_prefix', + ); + } + + String get unread_email_notification { + return Intl.message( + 'new', + name: 'unread_email_notification', + ); + } + + String get bcc_email_address_prefix { + return Intl.message( + 'Bcc', + name: 'bcc_email_address_prefix', + ); + } + + String get cc_email_address_prefix { + return Intl.message( + 'Cc', + name: 'cc_email_address_prefix', + ); + } } diff --git a/lib/main/pages/app_pages.dart b/lib/main/pages/app_pages.dart index 447473fa5d..05a9445f29 100644 --- a/lib/main/pages/app_pages.dart +++ b/lib/main/pages/app_pages.dart @@ -3,7 +3,6 @@ import 'package:tmail_ui_user/features/home/presentation/home_bindings.dart'; import 'package:tmail_ui_user/features/home/presentation/home_view.dart'; import 'package:tmail_ui_user/features/login/presentation/login_bindings.dart'; import 'package:tmail_ui_user/features/login/presentation/login_view.dart'; -import 'package:tmail_ui_user/features/email/presentation/email_bindings.dart'; import 'package:tmail_ui_user/features/email/presentation/email_view.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_view.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart'; @@ -39,7 +38,6 @@ class AppPages { page: () => ThreadView()), GetPage( name: AppRoutes.EMAIL, - page: () => EmailView(), - binding: EmailBindings()), + page: () => EmailView()), ]; }