From 3809a05a351aed05d41bda3dba6287dda5a3e92a Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 7 Sep 2023 09:53:34 +0700 Subject: [PATCH] TF-2116 Apply new design composer mobile (cherry picked from commit fe4ec144d1a18a94de782f87fff56bbf1cd3a00a) --- .../presentation/base_composer_view.dart | 505 -------------- .../presentation/composer_controller.dart | 8 +- .../composer/presentation/composer_view.dart | 659 ++++++++---------- .../presentation/composer_view_web.dart | 131 ++-- .../presentation/styles/composer_style.dart | 3 + ...bile_attachment_composer_widget_style.dart | 17 + ...blet_bottom_bar_composer_widget_style.dart | 25 + .../recipient_tag_item_widget_style.dart | 1 + .../attachment_composer_widget_style.dart | 0 .../bottom_bar_composer_widget_style.dart | 0 ...ktop_responsive_container_view_style.dart} | 2 +- ...bile_responsive_container_view_style.dart} | 2 +- ...blet_responsive_container_view_style.dart} | 2 +- .../presentation/view/editor_view_mixin.dart | 27 + .../desktop_responsive_container_view.dart} | 32 +- .../mobile_responsive_container_view.dart} | 8 +- .../tablet_responsive_container_view.dart} | 18 +- .../view/{ => web}/web_editor_view.dart | 32 +- .../mobile/app_bar_composer_widget.dart | 68 ++ .../landscape_app_bar_composer_widget.dart | 72 ++ .../mobile_attachment_composer_widget.dart | 86 +++ .../tablet_bottom_bar_composer_widget.dart | 65 ++ .../widgets/recipient_tag_item_widget.dart | 4 +- .../{ => web}/attachment_composer_widget.dart | 2 +- .../{ => web}/bottom_bar_composer_widget.dart | 2 +- .../desktop_app_bar_composer_widget.dart} | 6 +- .../{ => web}/minimize_composer_widget.dart | 0 ...e_responsive_app_bar_composer_widget.dart} | 4 +- .../{ => web}/toolbar_rich_text_builder.dart | 0 .../widgets/{ => web}/web_editor_widget.dart | 2 +- .../presentation/identity_creator_view.dart | 2 +- lib/l10n/intl_messages.arb | 12 +- lib/main/localizations/app_localizations.dart | 8 + 33 files changed, 803 insertions(+), 1002 deletions(-) delete mode 100644 lib/features/composer/presentation/base_composer_view.dart create mode 100644 lib/features/composer/presentation/styles/mobile/mobile_attachment_composer_widget_style.dart create mode 100644 lib/features/composer/presentation/styles/mobile/tablet_bottom_bar_composer_widget_style.dart rename lib/features/composer/presentation/styles/{ => web}/attachment_composer_widget_style.dart (100%) rename lib/features/composer/presentation/styles/{ => web}/bottom_bar_composer_widget_style.dart (100%) rename lib/features/composer/presentation/styles/{desktop_container_view_style.dart => web/desktop_responsive_container_view_style.dart} (85%) rename lib/features/composer/presentation/styles/{mobile_container_view_style.dart => web/mobile_responsive_container_view_style.dart} (80%) rename lib/features/composer/presentation/styles/{tablet_container_view_style.dart => web/tablet_responsive_container_view_style.dart} (95%) create mode 100644 lib/features/composer/presentation/view/editor_view_mixin.dart rename lib/features/composer/presentation/view/{desktop_container_view.dart => web/desktop_responsive_container_view.dart} (76%) rename lib/features/composer/presentation/view/{mobile_container_view.dart => web/mobile_responsive_container_view.dart} (70%) rename lib/features/composer/presentation/view/{tablet_container_view.dart => web/tablet_responsive_container_view.dart} (63%) rename lib/features/composer/presentation/view/{ => web}/web_editor_view.dart (85%) create mode 100644 lib/features/composer/presentation/widgets/mobile/app_bar_composer_widget.dart create mode 100644 lib/features/composer/presentation/widgets/mobile/landscape_app_bar_composer_widget.dart create mode 100644 lib/features/composer/presentation/widgets/mobile/mobile_attachment_composer_widget.dart create mode 100644 lib/features/composer/presentation/widgets/mobile/tablet_bottom_bar_composer_widget.dart rename lib/features/composer/presentation/widgets/{ => web}/attachment_composer_widget.dart (98%) rename lib/features/composer/presentation/widgets/{ => web}/bottom_bar_composer_widget.dart (99%) rename lib/features/composer/presentation/widgets/{app_bar_composer_widget.dart => web/desktop_app_bar_composer_widget.dart} (97%) rename lib/features/composer/presentation/widgets/{ => web}/minimize_composer_widget.dart (100%) rename lib/features/composer/presentation/widgets/{mobile_app_bar_composer_widget.dart => web/mobile_responsive_app_bar_composer_widget.dart} (97%) rename lib/features/composer/presentation/widgets/{ => web}/toolbar_rich_text_builder.dart (100%) rename lib/features/composer/presentation/widgets/{ => web}/web_editor_widget.dart (98%) diff --git a/lib/features/composer/presentation/base_composer_view.dart b/lib/features/composer/presentation/base_composer_view.dart deleted file mode 100644 index af0c21344..000000000 --- a/lib/features/composer/presentation/base_composer_view.dart +++ /dev/null @@ -1,505 +0,0 @@ -import 'package:core/presentation/extensions/capitalize_extension.dart'; -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/utils/app_toast.dart'; -import 'package:core/presentation/utils/responsive_utils.dart'; -import 'package:core/presentation/utils/style_utils.dart'; -import 'package:core/presentation/views/button/icon_button_web.dart'; -import 'package:core/presentation/views/button/tmail_button_widget.dart'; -import 'package:core/presentation/views/list/sliver_grid_delegate_fixed_height.dart'; -import 'package:core/presentation/views/text/text_field_builder.dart'; -import 'package:core/presentation/views/text/text_overflow_builder.dart'; -import 'package:core/utils/direction_utils.dart'; -import 'package:core/utils/platform_info.dart'; -import 'package:dropdown_button2/dropdown_button2.dart'; -import 'package:filesize/filesize.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:get/get.dart'; -import 'package:jmap_dart_client/jmap/identities/identity.dart'; -import 'package:model/email/prefix_email_address.dart'; -import 'package:model/mailbox/expand_mode.dart'; -import 'package:pointer_interceptor/pointer_interceptor.dart'; -import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; -import 'package:tmail_ui_user/features/base/widget/custom_scroll_behavior.dart'; -import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; -import 'package:tmail_ui_user/features/composer/presentation/mixin/composer_loading_mixin.dart'; -import 'package:tmail_ui_user/features/composer/presentation/mixin/rich_text_button_mixin.dart'; -import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart'; -import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_item_composer_widget.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart'; -import 'package:tmail_ui_user/features/upload/presentation/extensions/list_upload_file_state_extension.dart'; -import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; - -abstract class BaseComposerView extends GetWidget - with - AppLoaderMixin, - RichTextButtonMixin, - ComposerLoadingMixin { - - BaseComposerView({Key? key}) : super(key: key); - - final responsiveUtils = Get.find(); - final imagePaths = Get.find(); - final appToast = Get.find(); - - Widget buildFromEmailAddress(BuildContext context) { - return Obx(() { - return Padding( - padding: ComposerStyle.getFromAddressPadding(context, responsiveUtils), - child: Row(children: [ - Text( - '${AppLocalizations.of(context).from_email_address_prefix}:', - style: const TextStyle( - fontSize: 15, - color: AppColor.colorHintEmailAddressInput - ) - ), - SizedBox(width: ComposerStyle.getSpace(context, responsiveUtils)), - if (controller.listIdentities.isNotEmpty) - Padding( - padding: EdgeInsetsDirectional.only(end: ComposerStyle.getSpace(context, responsiveUtils)), - child: DropdownButtonHideUnderline( - child: DropdownButton2( - isExpanded: true, - customButton: SvgPicture.asset(imagePaths.icEditIdentity), - items: controller.listIdentities.map(_buildItemIdentity).toList(), - onChanged: controller.selectIdentity, - dropdownStyleData: DropdownStyleData( - maxHeight: 240, - width: PlatformInfo.isWeb ? 370 : 300, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: Colors.white), - elevation: 4, - scrollbarTheme: ScrollbarThemeData( - radius: const Radius.circular(40), - thickness: MaterialStateProperty.all(6), - thumbVisibility: MaterialStateProperty.all(true), - ), - ), - menuItemStyleData: const MenuItemStyleData( - height: 55, - padding: EdgeInsets.symmetric(horizontal: 8), - ) - ) - ), - ), - Expanded(child: TextOverflowBuilder( - controller.identitySelected.value != null - ? (controller.identitySelected.value?.email ?? '') - : (controller.userProfile?.email ?? ''), - style: const TextStyle( - fontSize: 17, - fontWeight: FontWeight.normal, - color: AppColor.colorEmailAddressPrefix - ) - )), - ]), - ); - }); - } - - DropdownMenuItem _buildItemIdentity(Identity identity) { - return DropdownMenuItem( - value: identity, - child: PointerInterceptor( - child: Container( - alignment: AlignmentDirectional.centerStart, - padding: const EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: identity == controller.identitySelected.value - ? AppColor.colorBgMenuItemDropDownSelected - : Colors.transparent - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - identity.name ?? '', - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.normal, - color: Colors.black - ), - maxLines: 1, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap, - ), - Text( - identity.email ?? '', - style: const TextStyle( - fontSize: 13, - fontWeight: FontWeight.normal, - color: AppColor.colorHintSearchBar - ), - maxLines: 1, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap, - ) - ] - ), - ), - ), - ); - } - - Widget buildEmailAddress(BuildContext context) { - return Obx(() => Column( - children: [ - RecipientComposerWidget( - prefix: PrefixEmailAddress.to, - listEmailAddress: controller.listToEmailAddress, - ccState: controller.ccRecipientState.value, - bccState: controller.bccRecipientState.value, - expandMode: controller.toAddressExpandMode.value, - controller: controller.toEmailAddressController, - focusNode: controller.toAddressFocusNode, - autoDisposeFocusNode: false, - keyTagEditor: controller.keyToEmailTagEditor, - isInitial: controller.isInitialRecipient.value, - nextFocusNode: controller.getNextFocusOfToEmailAddress(), - onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange, - onShowFullListEmailAddressAction: controller.showFullEmailAddress, - onAddEmailAddressTypeAction: controller.addEmailAddressType, - onUpdateListEmailAddressAction: controller.updateListEmailAddress, - onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, - onFocusNextAddressAction: controller.handleFocusNextAddressAction, - ), - if (controller.ccRecipientState.value == PrefixRecipientState.enabled) - RecipientComposerWidget( - prefix: PrefixEmailAddress.cc, - listEmailAddress: controller.listCcEmailAddress, - expandMode: controller.ccAddressExpandMode.value, - controller: controller.ccEmailAddressController, - focusNode: controller.ccAddressFocusNode, - autoDisposeFocusNode: false, - keyTagEditor: controller.keyCcEmailTagEditor, - isInitial: controller.isInitialRecipient.value, - nextFocusNode: controller.getNextFocusOfCcEmailAddress(), - onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange, - onShowFullListEmailAddressAction: controller.showFullEmailAddress, - onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType, - onUpdateListEmailAddressAction: controller.updateListEmailAddress, - onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, - onFocusNextAddressAction: controller.handleFocusNextAddressAction, - ), - if (controller.bccRecipientState.value == PrefixRecipientState.enabled) - RecipientComposerWidget( - prefix: PrefixEmailAddress.bcc, - listEmailAddress: controller.listBccEmailAddress, - expandMode: controller.bccAddressExpandMode.value, - controller: controller.bccEmailAddressController, - focusNode: controller.bccAddressFocusNode, - autoDisposeFocusNode: false, - keyTagEditor: controller.keyBccEmailTagEditor, - isInitial: controller.isInitialRecipient.value, - nextFocusNode: controller.subjectEmailInputFocusNode, - onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange, - onShowFullListEmailAddressAction: controller.showFullEmailAddress, - onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType, - onUpdateListEmailAddressAction: controller.updateListEmailAddress, - onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, - onFocusNextAddressAction: controller.handleFocusNextAddressAction, - ), - ], - )); - } - - Widget buildSubjectEmail(BuildContext context) { - return Padding( - padding: PlatformInfo.isWeb - ? ComposerStyle.getSubjectWebPadding(context, responsiveUtils) - : ComposerStyle.getSubjectPadding(context, responsiveUtils), - child: Row( - children: [ - Text( - '${AppLocalizations.of(context).subject_email}:', - style: const TextStyle( - fontSize: 15, - color: AppColor.colorHintEmailAddressInput - ) - ), - const SizedBox(width: 8), - Expanded(child: TextFieldBuilder( - key: const Key('subject_email_input'), - cursorColor: AppColor.colorTextButton, - focusNode: controller.subjectEmailInputFocusNode, - onTextChange: controller.setSubjectEmail, - maxLines: PlatformInfo.isWeb ? 1 : null, - textDirection: DirectionUtils.getDirectionByLanguage(context), - textStyle: const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal), - decoration: const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none), - controller: controller.subjectEmailInputController, - )) - ] - ), - ); - } - - Widget buildDivider() => const Divider(color: AppColor.colorLineComposer, height: 1); - - Widget buildAttachmentsWidget(BuildContext context) { - return Obx(() { - final uploadAttachments = controller.uploadController.listUploadAttachments; - if (uploadAttachments.isEmpty) { - return const SizedBox.shrink(); - } else { - return Padding( - padding: ComposerStyle.getAttachmentPadding(context, responsiveUtils), - child: Column( - children: [ - _buildAttachmentsTitle(context, - uploadAttachments, - controller.expandModeAttachments.value - ), - _buildAttachmentsList(context, - uploadAttachments, - controller.expandModeAttachments.value - ) - ] - ), - ); - } - }); - } - - Widget _buildAttachmentsTitle( - BuildContext context, - List uploadFilesState, - ExpandMode expandModeAttachment - ) { - return Row( - children: [ - Text( - '${AppLocalizations.of(context).attachments} (${filesize(uploadFilesState.totalSize, 0)}):', - style: const TextStyle( - fontSize: 12, - color: AppColor.colorHintEmailAddressInput, - fontWeight: FontWeight.normal - ) - ), - const Spacer(), - TMailButtonWidget.fromText( - text: expandModeAttachment == ExpandMode.EXPAND - ? AppLocalizations.of(context).hide - : '${AppLocalizations.of(context).showAll} (${uploadFilesState.length})', - onTapActionCallback: controller.toggleDisplayAttachments, - textStyle: const TextStyle( - fontWeight: FontWeight.w500, - fontSize: 12, - color: AppColor.colorTextButton - ), - backgroundColor: Colors.transparent, - ) - ], - ); - } - - Widget _buildAttachmentsList( - BuildContext context, - List uploadFilesState, - ExpandMode expandMode - ) { - const double maxHeightItem = 60; - if (expandMode == ExpandMode.EXPAND) { - if (PlatformInfo.isWeb) { - return LayoutBuilder(builder: (context, constraints) { - return Align( - alignment: AlignmentDirectional.centerStart, - child: SizedBox( - height: maxHeightItem, - child: ScrollConfiguration( - behavior: CustomScrollBehavior(), - child: ListView.builder( - key: const Key('list_attachment_minimize'), - shrinkWrap: true, - scrollDirection: Axis.horizontal, - controller: controller.scrollControllerAttachment, - itemCount: uploadFilesState.length, - itemBuilder: (context, index) => AttachmentItemComposerWidget( - fileState: uploadFilesState[index], - itemMargin: const EdgeInsetsDirectional.only(end: 8), - maxWidth: ComposerStyle.getMaxWidthItemListAttachment(context, constraints), - onDeleteAttachmentAction: (attachment) => controller.deleteAttachmentUploaded(attachment.uploadTaskId), - ) - ), - ) - ) - ); - }); - } else { - return LayoutBuilder(builder: (context, constraints) { - return GridView.builder( - key: const Key('list_attachment_full'), - primary: false, - shrinkWrap: true, - itemCount: uploadFilesState.length, - gridDelegate: SliverGridDelegateFixedHeight( - height: maxHeightItem, - crossAxisCount: ComposerStyle.getMaxItemRowListAttachment(context, constraints), - crossAxisSpacing: 8.0, - mainAxisSpacing: 8.0), - itemBuilder: (context, index) => AttachmentItemComposerWidget( - fileState: uploadFilesState[index], - onDeleteAttachmentAction: (attachment) => controller.deleteAttachmentUploaded(attachment.uploadTaskId) - ) - ); - }); - } - } else { - if (PlatformInfo.isWeb) { - return const SizedBox.shrink(); - } else { - return LayoutBuilder(builder: (context, constraints) { - return Align( - alignment: AlignmentDirectional.centerStart, - child: SizedBox( - height: maxHeightItem, - child: ListView.builder( - key: const Key('list_attachment_minimize'), - shrinkWrap: true, - physics: const ClampingScrollPhysics(), - scrollDirection: Axis.horizontal, - itemCount: uploadFilesState.length, - itemBuilder: (context, index) => AttachmentItemComposerWidget( - fileState: uploadFilesState[index], - itemMargin: const EdgeInsetsDirectional.only(end: 8), - maxWidth: ComposerStyle.getMaxWidthItemListAttachment(context, constraints), - onDeleteAttachmentAction: (attachment) => controller.deleteAttachmentUploaded(attachment.uploadTaskId) - ) - ) - ) - ); - }); - } - } - } - - Widget buildAppBar(BuildContext context) { - return Container( - height: ComposerStyle.getAppBarHeight(context, responsiveUtils), - padding: ComposerStyle.getAppBarPadding(context, responsiveUtils), - child: Row( - children: [ - buildIconWeb( - icon: SvgPicture.asset( - imagePaths.icClose, - width: 30, - height: 30, - fit: BoxFit.fill - ), - iconPadding: EdgeInsets.zero, - tooltip: AppLocalizations.of(context).saveAndClose, - onTap: () => controller.saveToDraftAndClose(context) - ), - Expanded(child: buildTitleComposer(context)), - if (responsiveUtils.isScreenWithShortestSide(context)) - Obx(() => buildIconWeb( - icon: SvgPicture.asset( - controller.isEnableEmailSendButton.value - ? imagePaths.icSendMobile - : imagePaths.icSendDisable, - fit: BoxFit.fill - ), - tooltip: AppLocalizations.of(context).send, - onTap: () => controller.sendEmailAction(context) - )), - if (responsiveUtils.isScreenWithShortestSide(context)) - buildIconWithLowerMenu( - SvgPicture.asset(imagePaths.icRequestReadReceipt), - context, - popUpMoreActionMenu(context), - controller.openPopupMenuAction - ), - ], - ), - ); - } - - List popUpMoreActionMenu(BuildContext context) { - return []; - } - - Widget buildTitleComposer(BuildContext context) { - return Obx(() => Text( - controller.subjectEmail.isNotEmpty == true - ? controller.subjectEmail.value ?? '' - : AppLocalizations.of(context).new_message.capitalizeFirstEach, - maxLines: 1, - textAlign: TextAlign.center, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.black - ), - )); - } - - Widget buildBottomBar(BuildContext context) { - return Stack( - alignment: Alignment.centerRight, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(width: 24), - buildTextButton( - AppLocalizations.of(context).cancel, - textStyle: const TextStyle( - fontWeight: FontWeight.w500, - fontSize: 17, - color: AppColor.colorLabelCancelButton - ), - backgroundColor: AppColor.emailAddressChipColor, - width: 150, - height: 44, - radius: 10, - onTap: () => controller.closeComposer(context) - ), - const SizedBox(width: 12), - buildTextButton( - AppLocalizations.of(context).save_to_drafts, - textStyle: const TextStyle( - fontWeight: FontWeight.w500, - fontSize: 17, - color: AppColor.colorTextButton - ), - backgroundColor: AppColor.emailAddressChipColor, - width: 150, - height: 44, - radius: 10, - onTap: () => controller.saveToDraftAction(context) - ), - const SizedBox(width: 12), - buildTextButton( - AppLocalizations.of(context).send, - width: 150, - height: 44, - radius: 10, - onTap: () => controller.sendEmailAction(context) - ), - const SizedBox(width: 24), - ] - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - buildIconWithUpperMenu( - SvgPicture.asset(imagePaths.icRequestReadReceipt), - context, - popUpMoreActionMenu(context), - controller.openPopupMenuAction - ) - ] - ), - ], - ); - } -} \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 9cc0c421f..a5f161d5a 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -53,6 +53,7 @@ import 'package:tmail_ui_user/features/composer/presentation/model/inline_image. import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/save_to_draft_view_event.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; +import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart'; import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/transform_html_email_content_state.dart'; @@ -139,8 +140,6 @@ class ComposerController extends BaseController { final GlobalKey keyBccEmailTagEditor = GlobalKey(); final GlobalKey headerEditorMobileWidgetKey = GlobalKey(); final double defaultPaddingCoordinateYCursorEditor = 8; - final double maxKeyBoardHeight = 500; - final double richTextBarHeight = 200; FocusNode? subjectEmailInputFocusNode; FocusNode? toAddressFocusNode; @@ -405,7 +404,7 @@ class ComposerController extends BaseController { }); } - void initRichTextForMobile(BuildContext context, HtmlEditorApi editorApi, String? content) { + void onCreatedMobileEditorAction(BuildContext context, HtmlEditorApi editorApi, String? content) { initTextEditor(content); richTextMobileTabletController.htmlEditorApi = editorApi; keyboardRichTextController.onCreateHTMLEditor( @@ -1753,7 +1752,8 @@ class ComposerController extends BaseController { if (coordinates?[1] != null && coordinates?[1] != 0) { final coordinateY = max((coordinates?[1] ?? 0) - defaultPaddingCoordinateYCursorEditor, 0); final realCoordinateY = coordinateY + (headerEditorMobileSize?.height ?? 0); - final webViewEditorClientY = max(Get.height - maxKeyBoardHeight - richTextBarHeight, 0) + scrollController.position.pixels; + final outsideHeight = Get.height - ComposerStyle.keyboardMaxHeight - ComposerStyle.keyboardToolBarHeight; + final webViewEditorClientY = max(outsideHeight, 0) + scrollController.position.pixels; if (scrollController.position.pixels >= realCoordinateY) { _scrollToCursorEditor( realCoordinateY.toDouble(), diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index 7650a5d16..cba1f133d 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -1,177 +1,330 @@ -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/extensions/html_extension.dart'; -import 'package:core/presentation/utils/html_transformer/html_utils.dart'; -import 'package:core/presentation/views/button/icon_button_web.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/views/context_menu/simple_context_menu_action_builder.dart'; -import 'package:core/presentation/views/image/avatar_builder.dart'; import 'package:core/presentation/views/responsive/responsive_widget.dart'; -import 'package:core/utils/platform_info.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:model/email/email_action_type.dart'; -import 'package:rich_text_composer/rich_text_composer.dart'; -import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart'; -import 'package:tmail_ui_user/features/base/widget/cupertino_loading_widget.dart'; -import 'package:tmail_ui_user/features/composer/presentation/base_composer_view.dart'; +import 'package:model/email/prefix_email_address.dart'; +import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; +import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart'; import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart'; -import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; -import 'package:tmail_ui_user/features/email/domain/state/transform_html_email_content_state.dart'; +import 'package:tmail_ui_user/features/composer/presentation/view/mobile/mobile_container_view.dart'; +import 'package:tmail_ui_user/features/composer/presentation/view/mobile/mobile_editor_view.dart'; +import 'package:tmail_ui_user/features/composer/presentation/view/mobile/tablet_container_view.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/mobile_attachment_composer_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/app_bar_composer_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/landscape_app_bar_composer_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/tablet_bottom_bar_composer_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/subject_composer_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/web/desktop_app_bar_composer_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -import 'package:tmail_ui_user/main/utils/app_utils.dart'; -class ComposerView extends BaseComposerView { +class ComposerView extends GetWidget { + + final _responsiveUtils = Get.find(); + final _imagePaths = Get.find(); ComposerView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return ResponsiveWidget( - responsiveUtils: responsiveUtils, - mobile: _buildComposerViewForMobile(context), - tablet: _buildComposerViewForTablet(context), - ); - } - - Widget _buildComposerViewForMobile(BuildContext context) { - return WillPopScope( - onWillPop: () async { - controller.saveToDraftAndClose(context, canPop: false); - return true; - }, - child: GestureDetector( - onTap: () => controller.clearFocusEditor(context), - child: Scaffold( - backgroundColor: Colors.white, - resizeToAvoidBottomInset: false, - body: LayoutBuilder(builder: (context, constraints) { - return KeyboardVisibilityBuilder(builder: (context, isKeyboardVisible) { - return KeyboardRichText( - richTextController: controller.keyboardRichTextController, - keyBroadToolbar: RichTextKeyboardToolBar( - backgroundKeyboardToolBarColor: PlatformInfo.isIOS - ? AppColor.colorBackgroundKeyboard - : AppColor.colorBackgroundKeyboardAndroid, - isLandScapeMode: responsiveUtils.isLandscapeMobile(context), - insertAttachment: controller.isNetworkConnectionAvailable - ? () => controller.openPickAttachmentMenu(context, _pickAttachmentsActionTiles(context)) - : null, - insertImage: controller.isNetworkConnectionAvailable - ? () => controller.insertImage(context, constraints.maxWidth) - : null, - richTextController: controller.keyboardRichTextController, - titleQuickStyleBottomSheet: AppLocalizations.of(context).titleQuickStyles, - titleBackgroundBottomSheet: AppLocalizations.of(context).titleBackground, - titleForegroundBottomSheet: AppLocalizations.of(context).titleForeground, - titleFormatBottomSheet: AppLocalizations.of(context).titleFormat, - titleBack: AppLocalizations.of(context).format, - ), - paddingChild: isKeyboardVisible ? const EdgeInsets.only(bottom: 64) : EdgeInsets.zero, - child: SafeArea( - child: Container( - color: Colors.white, - child: Column(children: [ - buildAppBar(context), - buildDivider(), - Expanded(child: _buildBodyMobile(context)) - ]) - ), - ), - ); - }); - }) - ), - ) - ); - } - - Widget _buildComposerViewForTablet(BuildContext context) { - return WillPopScope( - onWillPop: () async { - controller.saveToDraftAndClose(context, canPop: false); - return true; - }, - child: GestureDetector( - onTap: () => controller.clearFocusEditor(context), - child: Scaffold( - backgroundColor: Colors.black38, - body: LayoutBuilder(builder: (context, constraints) { - return KeyboardVisibilityBuilder(builder: (context, isKeyboardVisible) { - return KeyboardRichText( - richTextController: controller.keyboardRichTextController, - keyBroadToolbar: RichTextKeyboardToolBar( - backgroundKeyboardToolBarColor: PlatformInfo.isIOS - ? AppColor.colorBackgroundKeyboard - : AppColor.colorBackgroundKeyboardAndroid, - insertAttachment: () => controller.openPickAttachmentMenu(context, _pickAttachmentsActionTiles(context)), - insertImage: () => controller.insertImage(context, constraints.maxWidth), - richTextController: controller.keyboardRichTextController, - titleQuickStyleBottomSheet: AppLocalizations.of(context).titleQuickStyles, - titleBackgroundBottomSheet: AppLocalizations.of(context).titleBackground, - titleForegroundBottomSheet: AppLocalizations.of(context).titleForeground, - titleFormatBottomSheet: AppLocalizations.of(context).titleFormat, - titleBack: AppLocalizations.of(context).format, - ), - paddingChild: isKeyboardVisible ? const EdgeInsets.only(bottom: 64) : EdgeInsets.zero, - child: Center( - child: SafeArea( - child: Card( - elevation: 20, - margin: ComposerStyle.getMarginForTablet(context, responsiveUtils), - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(ComposerStyle.radius))), - child: Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(ComposerStyle.radius))), - width: ComposerStyle.getWidthForTablet(context, responsiveUtils), - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(ComposerStyle.radius)), - child: Column(children: [ - buildAppBar(context), - buildDivider(), - Expanded(child: _buildBodyTablet(context)), - buildDivider(), - Padding( - padding: const EdgeInsetsDirectional.symmetric(vertical: 16), - child: buildBottomBar(context)), - ]) - ) - ) - ), + responsiveUtils: _responsiveUtils, + mobile: MobileContainerView( + keyboardRichTextController: controller.keyboardRichTextController, + onCloseViewAction: () => controller.saveToDraftAndClose(context, canPop: false), + onClearFocusAction: () => controller.clearFocusEditor(context), + onAttachFileAction: () => controller.isNetworkConnectionAvailable + ? controller.openPickAttachmentMenu( + context, + _pickAttachmentsActionTiles(context) + ) + : null, + onInsertImageAction: (constraints) => controller.isNetworkConnectionAvailable + ? controller.insertImage(context, constraints.maxWidth) + : null, + childBuilder: (context) => SafeArea( + left: !_responsiveUtils.isLandscapeMobile(context), + right: !_responsiveUtils.isLandscapeMobile(context), + child: Container( + color: ComposerStyle.mobileBackgroundColor, + child: Column( + children: [ + if (_responsiveUtils.isLandscapeMobile(context)) + Obx(() => LandscapeAppBarComposerWidget( + isSendButtonEnabled: controller.isEnableEmailSendButton.value, + onCloseViewAction: () => controller.saveToDraftAndClose(context), + sendMessageAction: () => controller.sendEmailAction(context), + openContextMenuAction: () => {}, + )) + else + Obx(() => AppBarComposerWidget( + isSendButtonEnabled: controller.isEnableEmailSendButton.value, + onCloseViewAction: () => controller.saveToDraftAndClose(context), + sendMessageAction: () => controller.sendEmailAction(context), + openContextMenuAction: () => {}, )), - ); - }); - }) - ) - ) + Expanded( + child: SafeArea( + top: false, + bottom: false, + child: SingleChildScrollView( + controller: controller.scrollController, + physics: const ClampingScrollPhysics(), + child: Column( + children: [ + Obx(() => Column( + children: [ + RecipientComposerWidget( + prefix: PrefixEmailAddress.to, + listEmailAddress: controller.listToEmailAddress, + ccState: controller.ccRecipientState.value, + bccState: controller.bccRecipientState.value, + expandMode: controller.toAddressExpandMode.value, + controller: controller.toEmailAddressController, + focusNode: controller.toAddressFocusNode, + autoDisposeFocusNode: false, + keyTagEditor: controller.keyToEmailTagEditor, + isInitial: controller.isInitialRecipient.value, + padding: ComposerStyle.mobileRecipientPadding, + margin: ComposerStyle.mobileRecipientMargin, + nextFocusNode: controller.getNextFocusOfToEmailAddress(), + onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange, + onShowFullListEmailAddressAction: controller.showFullEmailAddress, + onAddEmailAddressTypeAction: controller.addEmailAddressType, + onUpdateListEmailAddressAction: controller.updateListEmailAddress, + onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, + onFocusNextAddressAction: controller.handleFocusNextAddressAction, + ), + if (controller.ccRecipientState.value == PrefixRecipientState.enabled) + RecipientComposerWidget( + prefix: PrefixEmailAddress.cc, + listEmailAddress: controller.listCcEmailAddress, + expandMode: controller.ccAddressExpandMode.value, + controller: controller.ccEmailAddressController, + focusNode: controller.ccAddressFocusNode, + autoDisposeFocusNode: false, + keyTagEditor: controller.keyCcEmailTagEditor, + isInitial: controller.isInitialRecipient.value, + nextFocusNode: controller.getNextFocusOfCcEmailAddress(), + padding: ComposerStyle.mobileRecipientPadding, + margin: ComposerStyle.mobileRecipientMargin, + onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange, + onShowFullListEmailAddressAction: controller.showFullEmailAddress, + onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType, + onUpdateListEmailAddressAction: controller.updateListEmailAddress, + onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, + onFocusNextAddressAction: controller.handleFocusNextAddressAction, + ), + if (controller.bccRecipientState.value == PrefixRecipientState.enabled) + RecipientComposerWidget( + prefix: PrefixEmailAddress.bcc, + listEmailAddress: controller.listBccEmailAddress, + expandMode: controller.bccAddressExpandMode.value, + controller: controller.bccEmailAddressController, + focusNode: controller.bccAddressFocusNode, + autoDisposeFocusNode: false, + keyTagEditor: controller.keyBccEmailTagEditor, + isInitial: controller.isInitialRecipient.value, + nextFocusNode: controller.subjectEmailInputFocusNode, + padding: ComposerStyle.mobileRecipientPadding, + margin: ComposerStyle.mobileRecipientMargin, + onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange, + onShowFullListEmailAddressAction: controller.showFullEmailAddress, + onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType, + onUpdateListEmailAddressAction: controller.updateListEmailAddress, + onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, + onFocusNextAddressAction: controller.handleFocusNextAddressAction, + ), + ], + )), + SubjectComposerWidget( + focusNode: controller.subjectEmailInputFocusNode, + textController: controller.subjectEmailInputController, + onTextChange: controller.setSubjectEmail, + padding: ComposerStyle.mobileSubjectPadding, + margin: ComposerStyle.mobileSubjectMargin, + ), + Obx(() => GestureDetector( + onTapDown: (_) { + controller.removeFocusAllInputEditorHeader(); + }, + child: Padding( + padding: ComposerStyle.mobileEditorPadding, + child: MobileEditorView( + arguments: controller.composerArguments.value, + contentViewState: controller.emailContentsViewState.value, + onCreatedEditorAction: controller.onCreatedMobileEditorAction, + ), + ), + )), + Obx(() { + if (controller.uploadController.listUploadAttachments.isNotEmpty) { + return MobileAttachmentComposerWidget( + listFileUploaded: controller.uploadController.listUploadAttachments, + isShowMore: controller.isAttachmentCollapsed, + onDeleteAttachmentAction: (fileState) => controller.deleteAttachmentUploaded(fileState.uploadTaskId), + onShowMoreAttachmentAction: (isShowMore) => controller.isAttachmentCollapsed = !isShowMore, + ); + } else { + return const SizedBox.shrink(); + } + }), + const SizedBox(height: ComposerStyle.keyboardMaxHeight), + ], + ), + ), + ) + ) + ]) + ), + ), + ), + tablet: TabletContainerView( + keyboardRichTextController: controller.keyboardRichTextController, + onCloseViewAction: () => controller.saveToDraftAndClose(context, canPop: false), + onClearFocusAction: () => controller.clearFocusEditor(context), + onAttachFileAction: () => controller.isNetworkConnectionAvailable + ? controller.openPickAttachmentMenu( + context, + _pickAttachmentsActionTiles(context) + ) + : null, + onInsertImageAction: (constraints) => controller.isNetworkConnectionAvailable + ? controller.insertImage(context, constraints.maxWidth) + : null, + childBuilder: (context, constraints) => Container( + color: ComposerStyle.mobileBackgroundColor, + child: Column( + children: [ + Obx(() => DesktopAppBarComposerWidget( + emailSubject: controller.subjectEmail.value ?? '', + onCloseViewAction: () => controller.saveToDraftAndClose(context), + constraints: constraints, + )), + Expanded( + child: SingleChildScrollView( + controller: controller.scrollController, + physics: const ClampingScrollPhysics(), + child: Column( + children: [ + Obx(() => Column( + children: [ + RecipientComposerWidget( + prefix: PrefixEmailAddress.to, + listEmailAddress: controller.listToEmailAddress, + ccState: controller.ccRecipientState.value, + bccState: controller.bccRecipientState.value, + expandMode: controller.toAddressExpandMode.value, + controller: controller.toEmailAddressController, + focusNode: controller.toAddressFocusNode, + autoDisposeFocusNode: false, + keyTagEditor: controller.keyToEmailTagEditor, + isInitial: controller.isInitialRecipient.value, + padding: ComposerStyle.mobileRecipientPadding, + margin: ComposerStyle.mobileRecipientMargin, + nextFocusNode: controller.getNextFocusOfToEmailAddress(), + onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange, + onShowFullListEmailAddressAction: controller.showFullEmailAddress, + onAddEmailAddressTypeAction: controller.addEmailAddressType, + onUpdateListEmailAddressAction: controller.updateListEmailAddress, + onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, + onFocusNextAddressAction: controller.handleFocusNextAddressAction, + ), + if (controller.ccRecipientState.value == PrefixRecipientState.enabled) + RecipientComposerWidget( + prefix: PrefixEmailAddress.cc, + listEmailAddress: controller.listCcEmailAddress, + expandMode: controller.ccAddressExpandMode.value, + controller: controller.ccEmailAddressController, + focusNode: controller.ccAddressFocusNode, + autoDisposeFocusNode: false, + keyTagEditor: controller.keyCcEmailTagEditor, + isInitial: controller.isInitialRecipient.value, + nextFocusNode: controller.getNextFocusOfCcEmailAddress(), + padding: ComposerStyle.mobileRecipientPadding, + margin: ComposerStyle.mobileRecipientMargin, + onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange, + onShowFullListEmailAddressAction: controller.showFullEmailAddress, + onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType, + onUpdateListEmailAddressAction: controller.updateListEmailAddress, + onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, + onFocusNextAddressAction: controller.handleFocusNextAddressAction, + ), + if (controller.bccRecipientState.value == PrefixRecipientState.enabled) + RecipientComposerWidget( + prefix: PrefixEmailAddress.bcc, + listEmailAddress: controller.listBccEmailAddress, + expandMode: controller.bccAddressExpandMode.value, + controller: controller.bccEmailAddressController, + focusNode: controller.bccAddressFocusNode, + autoDisposeFocusNode: false, + keyTagEditor: controller.keyBccEmailTagEditor, + isInitial: controller.isInitialRecipient.value, + nextFocusNode: controller.subjectEmailInputFocusNode, + padding: ComposerStyle.mobileRecipientPadding, + margin: ComposerStyle.mobileRecipientMargin, + onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange, + onShowFullListEmailAddressAction: controller.showFullEmailAddress, + onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType, + onUpdateListEmailAddressAction: controller.updateListEmailAddress, + onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, + onFocusNextAddressAction: controller.handleFocusNextAddressAction, + ), + ], + )), + SubjectComposerWidget( + focusNode: controller.subjectEmailInputFocusNode, + textController: controller.subjectEmailInputController, + onTextChange: controller.setSubjectEmail, + padding: ComposerStyle.mobileSubjectPadding, + margin: ComposerStyle.mobileSubjectMargin, + ), + Obx(() => GestureDetector( + onTapDown: (_) { + controller.removeFocusAllInputEditorHeader(); + }, + child: Padding( + padding: ComposerStyle.mobileEditorPadding, + child: MobileEditorView( + arguments: controller.composerArguments.value, + contentViewState: controller.emailContentsViewState.value, + onCreatedEditorAction: controller.onCreatedMobileEditorAction, + ), + ), + )), + Obx(() { + if (controller.uploadController.listUploadAttachments.isNotEmpty) { + return MobileAttachmentComposerWidget( + listFileUploaded: controller.uploadController.listUploadAttachments, + isShowMore: controller.isAttachmentCollapsed, + onDeleteAttachmentAction: (fileState) => controller.deleteAttachmentUploaded(fileState.uploadTaskId), + onShowMoreAttachmentAction: (isShowMore) => controller.isAttachmentCollapsed = !isShowMore, + ); + } else { + return const SizedBox.shrink(); + } + }) + ], + ), + ) + ), + TabletBottomBarComposerWidget( + deleteComposerAction: () => controller.closeComposer(context), + saveToDraftAction: () => controller.saveToDraftAction(context), + sendMessageAction: () => controller.sendEmailAction(context), + ), + ] + ) + ), + ), ); } - @override - List popUpMoreActionMenu(BuildContext context) { - return [ - PopupMenuItem( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Row( - children: [ - Obx(() => buildIconWeb( - icon: Icon(controller.hasRequestReadReceipt.value ? Icons.done : null, color: Colors.black))), - IgnorePointer( - child: buildTextIcon( - AppLocalizations.of(context).requestReadReceipt, - textStyle: const TextStyle(color: Colors.black, fontSize: 15)), - ), - ]), - onTap: () { - controller.toggleRequestReadReceipt(); - }, - ) - ]; - } - List _pickAttachmentsActionTiles(BuildContext context) { return [ _pickPhotoAndVideoAction(context), @@ -183,7 +336,7 @@ class ComposerView extends BaseComposerView { Widget _pickPhotoAndVideoAction(BuildContext context) { return (SimpleContextMenuActionBuilder( const Key('pick_photo_and_video_context_menu_action'), - SvgPicture.asset(imagePaths.icPhotoLibrary, width: 24, height: 24, fit: BoxFit.fill), + SvgPicture.asset(_imagePaths.icPhotoLibrary, width: 24, height: 24, fit: BoxFit.fill), AppLocalizations.of(context).photos_and_videos) ..onActionClick((_) => controller.openFilePickerByType(context, FileType.media))) .build(); @@ -192,203 +345,9 @@ class ComposerView extends BaseComposerView { Widget _browseFileAction(BuildContext context) { return (SimpleContextMenuActionBuilder( const Key('browse_file_context_menu_action'), - SvgPicture.asset(imagePaths.icMore, width: 24, height: 24, fit: BoxFit.fill), + SvgPicture.asset(_imagePaths.icMore, width: 24, height: 24, fit: BoxFit.fill), AppLocalizations.of(context).browse) ..onActionClick((_) => controller.openFilePickerByType(context, FileType.any))) .build(); } - - Widget _buildBodyMobile(BuildContext context) { - return SingleChildScrollView( - controller: controller.scrollController, - physics: const ClampingScrollPhysics(), - child: Column( - children: [ - _buildHeaderEditorMobile(context), - _buildComposerEditor(context), - SizedBox(height: controller.maxKeyBoardHeight), - ], - ), - ); - } - - Widget _buildHeaderEditorMobile(BuildContext context) { - return Column( - key: controller.headerEditorMobileWidgetKey, - children: [ - buildFromEmailAddress(context), - buildDivider(), - buildEmailAddress(context), - buildDivider(), - buildSubjectEmail(context), - buildDivider(), - buildAttachmentsWidget(context), - buildInlineLoadingView(controller), - ], - ); - } - - Widget _buildBodyTablet(BuildContext context) { - return SingleChildScrollView( - controller: controller.scrollController, - physics: const ClampingScrollPhysics(), - child: Column(children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(width: 16), - Padding( - padding: const EdgeInsets.only(top: 20), - child: (AvatarBuilder() - ..text(controller.mailboxDashBoardController.userProfile.value?.getAvatarText() ?? '') - ..size(56) - ..addTextStyle(const TextStyle( - fontWeight: FontWeight.w600, - fontSize: 28, - color: Colors.white - )) - ..backgroundColor(AppColor.colorAvatar) - ).build() - ), - const SizedBox(width: 16), - Expanded(child: Column(children: [ - buildFromEmailAddress(context), - buildDivider(), - buildEmailAddress(context), - buildDivider(), - buildSubjectEmail(context), - ])) - ]), - buildDivider(), - buildAttachmentsWidget(context), - buildInlineLoadingView(controller), - _buildComposerEditor(context), - ]) - ); - } - - Widget _buildComposerEditor(BuildContext context) { - return Obx(() { - final argsComposer = controller.composerArguments.value; - - if (argsComposer == null) { - return const SizedBox.shrink(); - } - - switch(argsComposer.emailActionType) { - case EmailActionType.compose: - case EmailActionType.composeFromEmailAddress: - case EmailActionType.composeFromMailtoURL: - case EmailActionType.composeFromFileShared: - return _buildHtmlEditor( - context, - initialContent: HtmlExtension.editorStartTags - ); - case EmailActionType.editDraft: - case EmailActionType.editSendingEmail: - case EmailActionType.composeFromContentShared: - case EmailActionType.reopenComposerBrowser: - final emailContentsViewState = controller.emailContentsViewState.value; - if (emailContentsViewState == null) { - return const SizedBox.shrink(); - } - return emailContentsViewState.fold( - (failure) => _buildHtmlEditor( - context, - initialContent: HtmlExtension.editorStartTags - ), - (success) { - if (success is GetEmailContentLoading) { - return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0)); - } else if (success is GetEmailContentSuccess) { - var contentHtml = success.htmlEmailContent; - if (contentHtml.isEmpty == true) { - contentHtml = HtmlExtension.editorStartTags; - } - return _buildHtmlEditor( - context, - initialContent: contentHtml - ); - } else { - return _buildHtmlEditor( - context, - initialContent: HtmlExtension.editorStartTags - ); - } - }); - case EmailActionType.reply: - case EmailActionType.replyAll: - case EmailActionType.forward: - final emailContentsViewState = controller.emailContentsViewState.value; - if (emailContentsViewState == null) { - return const SizedBox.shrink(); - } - return emailContentsViewState.fold( - (failure) { - final emailContentQuoted = controller.getEmailContentQuotedAsHtml( - context: context, - emailContent: '', - emailActionType: argsComposer.emailActionType, - presentationEmail: argsComposer.presentationEmail! - ); - return _buildHtmlEditor( - context, - initialContent: emailContentQuoted - ); - }, - (success) { - if (success is TransformHtmlEmailContentLoading) { - return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0)); - } else if (success is TransformHtmlEmailContentSuccess) { - final emailContentQuoted = controller.getEmailContentQuotedAsHtml( - context: context, - emailContent: success.htmlContent, - emailActionType: argsComposer.emailActionType, - presentationEmail: argsComposer.presentationEmail! - ); - return _buildHtmlEditor( - context, - initialContent: emailContentQuoted - ); - } else { - final emailContentQuoted = controller.getEmailContentQuotedAsHtml( - context: context, - emailContent: '', - emailActionType: argsComposer.emailActionType, - presentationEmail: argsComposer.presentationEmail! - ); - return _buildHtmlEditor( - context, - initialContent: emailContentQuoted - ); - } - } - ); - default: - return _buildHtmlEditor( - context, - initialContent: HtmlExtension.editorStartTags - ); - } - }); - } - - Widget _buildHtmlEditor(BuildContext context, {String? initialContent}) { - return GestureDetector( - onTapDown: (_) { - controller.removeFocusAllInputEditorHeader(); - }, - child: Padding( - padding: ComposerStyle.getEditorPadding(context, responsiveUtils), - child: HtmlEditor( - key: const Key('composer_editor'), - minHeight: 550, - addDefaultSelectionMenuItems: false, - initialContent: initialContent ?? '', - customStyleCss: HtmlUtils.customCssStyleHtmlEditor(direction: AppUtils.getCurrentDirection(context)), - onCreated: (editorApi) => controller.initRichTextForMobile(context, editorApi, initialContent) - ), - ), - ); - } } \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index 4ac79b6ab..f84cec342 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -7,17 +7,17 @@ import 'package:model/email/prefix_email_address.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart'; import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart'; -import 'package:tmail_ui_user/features/composer/presentation/view/desktop_container_view.dart'; -import 'package:tmail_ui_user/features/composer/presentation/view/mobile_container_view.dart'; -import 'package:tmail_ui_user/features/composer/presentation/view/tablet_container_view.dart'; -import 'package:tmail_ui_user/features/composer/presentation/view/web_editor_view.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/app_bar_composer_widget.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_composer_widget.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/bottom_bar_composer_widget.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile_app_bar_composer_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/view/web/desktop_responsive_container_view.dart'; +import 'package:tmail_ui_user/features/composer/presentation/view/web/mobile_responsive_container_view.dart'; +import 'package:tmail_ui_user/features/composer/presentation/view/web/tablet_responsive_container_view.dart'; +import 'package:tmail_ui_user/features/composer/presentation/view/web/web_editor_view.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/web/desktop_app_bar_composer_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/web/attachment_composer_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/web/bottom_bar_composer_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/web/mobile_responsive_app_bar_composer_widget.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/subject_composer_widget.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/toolbar_rich_text_builder.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/web/toolbar_rich_text_builder.dart'; class ComposerView extends GetWidget { @@ -29,12 +29,12 @@ class ComposerView extends GetWidget { Widget build(BuildContext context) { return ResponsiveWidget( responsiveUtils: _responsiveUtils, - mobile: MobileContainerView( + mobile: MobileResponsiveContainerView( childBuilder: (context, constraints) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Obx(() => MobileAppBarComposerWidget( + Obx(() => MobileResponsiveAppBarComposerWidget( isCodeViewEnabled: controller.richTextWebController.codeViewEnabled, isFormattingOptionsEnabled: controller.richTextWebController.isFormattingOptionsEnabled, openRichToolbarAction: controller.richTextWebController.toggleFormattingOptions, @@ -129,74 +129,57 @@ class ComposerView extends GetWidget { padding: ComposerStyle.mobileSubjectPadding, margin: ComposerStyle.mobileSubjectMargin, ), - Expanded( - child: Container( - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide( - color: ComposerStyle.borderColor, - width: 1 - ) + Obx(() => Expanded( + child: Padding( + padding: ComposerStyle.mobileEditorPadding, + child: WebEditorView( + editorController: controller.richTextWebController.editorController, + arguments: controller.composerArguments.value, + contentViewState: controller.emailContentsViewState.value, + currentWebContent: controller.textEditorWeb, + onInitial: controller.handleInitHtmlEditorWeb, + onChangeContent: controller.onChangeTextEditorWeb, + onFocus: controller.handleOnFocusHtmlEditorWeb, + onUnFocus: controller.handleOnUnFocusHtmlEditorWeb, + onMouseDown: controller.handleOnMouseDownHtmlEditorWeb, + onEditorSettings: controller.richTextWebController.onEditorSettingsChange, + ), + ), + )), + Obx(() { + if (controller.uploadController.listUploadAttachments.isNotEmpty) { + return AttachmentComposerWidget( + listFileUploaded: controller.uploadController.listUploadAttachments, + isCollapsed: controller.isAttachmentCollapsed, + onDeleteAttachmentAction: (fileState) => controller.deleteAttachmentUploaded(fileState.uploadTaskId), + onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed, + ); + } else { + return const SizedBox.shrink(); + } + }), + Obx(() { + if (controller.richTextWebController.isFormattingOptionsEnabled) { + return ToolbarRichTextWebBuilder( + richTextWebController: controller.richTextWebController, + padding: ComposerStyle.richToolbarPadding, + decoration: const BoxDecoration( + color: ComposerStyle.richToolbarColor, + boxShadow: ComposerStyle.richToolbarShadow ), - color: ComposerStyle.backgroundEditorColor - ), - child: Column( - children: [ - Obx(() => Expanded( - child: Padding( - padding: ComposerStyle.mobileEditorPadding, - child: WebEditorView( - editorController: controller.richTextWebController.editorController, - arguments: controller.composerArguments.value, - contentViewState: controller.emailContentsViewState.value, - currentWebContent: controller.textEditorWeb, - onInitial: controller.handleInitHtmlEditorWeb, - onChangeContent: controller.onChangeTextEditorWeb, - onFocus: controller.handleOnFocusHtmlEditorWeb, - onUnFocus: controller.handleOnUnFocusHtmlEditorWeb, - onMouseDown: controller.handleOnMouseDownHtmlEditorWeb, - onEditorSettings: controller.richTextWebController.onEditorSettingsChange, - ), - ), - )), - Obx(() { - if (controller.uploadController.listUploadAttachments.isNotEmpty) { - return AttachmentComposerWidget( - listFileUploaded: controller.uploadController.listUploadAttachments, - isCollapsed: controller.isAttachmentCollapsed, - onDeleteAttachmentAction: (fileState) => controller.deleteAttachmentUploaded(fileState.uploadTaskId), - onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed, - ); - } else { - return const SizedBox.shrink(); - } - }), - Obx(() { - if (controller.richTextWebController.isFormattingOptionsEnabled) { - return ToolbarRichTextWebBuilder( - richTextWebController: controller.richTextWebController, - padding: ComposerStyle.richToolbarPadding, - decoration: const BoxDecoration( - color: ComposerStyle.richToolbarColor, - boxShadow: ComposerStyle.richToolbarShadow - ), - ); - } else { - return const SizedBox.shrink(); - } - }) - ], - ), - ) - ), + ); + } else { + return const SizedBox.shrink(); + } + }) ] ); } ), - desktop: Obx(() => DesktopContainerView( + desktop: Obx(() => DesktopResponsiveContainerView( childBuilder: (context, constraints) { return Column(children: [ - Obx(() => AppBarComposerWidget( + Obx(() => DesktopAppBarComposerWidget( emailSubject: controller.subjectEmail.value ?? '', displayMode: controller.screenDisplayMode.value, onCloseViewAction: () => controller.saveToDraftAndClose(context), @@ -365,10 +348,10 @@ class ComposerView extends GetWidget { onCloseViewAction: () => controller.saveToDraftAndClose(context), onChangeDisplayModeAction: controller.displayScreenTypeComposerAction, )), - tablet: TabletContainerView( + tablet: TabletResponsiveContainerView( childBuilder: (context, constraints) { return Column(children: [ - Obx(() => AppBarComposerWidget( + Obx(() => DesktopAppBarComposerWidget( emailSubject: controller.subjectEmail.value ?? '', onCloseViewAction: () => controller.saveToDraftAndClose(context), constraints: constraints, diff --git a/lib/features/composer/presentation/styles/composer_style.dart b/lib/features/composer/presentation/styles/composer_style.dart index ee6b16931..875ab98b3 100644 --- a/lib/features/composer/presentation/styles/composer_style.dart +++ b/lib/features/composer/presentation/styles/composer_style.dart @@ -6,10 +6,13 @@ import 'package:flutter/material.dart'; class ComposerStyle { static const double radius = 28; + static const double keyboardMaxHeight = 500; + static const double keyboardToolBarHeight = 200; static const Color borderColor = AppColor.colorLineComposer; static const Color backgroundEditorColor = Colors.white; static const Color richToolbarColor = Colors.white; + static const Color mobileBackgroundColor = Colors.white; static const EdgeInsetsGeometry richToolbarPadding = EdgeInsetsDirectional.symmetric(horizontal: 24, vertical: 8); static const EdgeInsetsGeometry desktopRecipientPadding = EdgeInsetsDirectional.only(end: 24); diff --git a/lib/features/composer/presentation/styles/mobile/mobile_attachment_composer_widget_style.dart b/lib/features/composer/presentation/styles/mobile/mobile_attachment_composer_widget_style.dart new file mode 100644 index 000000000..ef83ffd0c --- /dev/null +++ b/lib/features/composer/presentation/styles/mobile/mobile_attachment_composer_widget_style.dart @@ -0,0 +1,17 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:flutter/material.dart'; + +class MobileAttachmentComposerWidgetStyle { + static const double listItemSpace = 8; + + static const Color backgroundColor = Colors.white; + + static const EdgeInsetsGeometry padding = EdgeInsetsDirectional.symmetric(vertical: 8, horizontal: 16); + static const EdgeInsetsGeometry showMoreMargin = EdgeInsetsDirectional.only(top: 8); + + static const TextStyle showMoreButtonTextStyle = TextStyle( + fontSize: 15, + color: AppColor.primaryColor, + fontWeight: FontWeight.w500 + ); +} \ No newline at end of file diff --git a/lib/features/composer/presentation/styles/mobile/tablet_bottom_bar_composer_widget_style.dart b/lib/features/composer/presentation/styles/mobile/tablet_bottom_bar_composer_widget_style.dart new file mode 100644 index 000000000..88fe07647 --- /dev/null +++ b/lib/features/composer/presentation/styles/mobile/tablet_bottom_bar_composer_widget_style.dart @@ -0,0 +1,25 @@ + +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:flutter/material.dart'; + +class TabletBottomBarComposerWidgetStyle { + static const double iconRadius = 8; + static const double space = 10; + static const double sendButtonSpace = 12; + static const double iconSize = 20; + static const double sendButtonRadius = 8; + static const double sendButtonIconSpace = 5; + + static const Color backgroundColor = Colors.white; + static const Color iconColor = AppColor.colorRichButtonComposer; + static const Color sendButtonBackgroundColor = AppColor.primaryColor; + + static const EdgeInsetsGeometry padding = EdgeInsetsDirectional.symmetric(horizontal: 32, vertical: 12); + static const EdgeInsetsGeometry iconPadding = EdgeInsetsDirectional.all(5); + static const EdgeInsetsGeometry sendButtonPadding = EdgeInsetsDirectional.symmetric(vertical: 8, horizontal: 24); + static const TextStyle sendButtonTextStyle = TextStyle( + fontWeight: FontWeight.w500, + fontSize: 15, + color: Colors.white, + ); +} \ No newline at end of file diff --git a/lib/features/composer/presentation/styles/recipient_tag_item_widget_style.dart b/lib/features/composer/presentation/styles/recipient_tag_item_widget_style.dart index ac86ed8bf..d548a6878 100644 --- a/lib/features/composer/presentation/styles/recipient_tag_item_widget_style.dart +++ b/lib/features/composer/presentation/styles/recipient_tag_item_widget_style.dart @@ -6,6 +6,7 @@ class RecipientTagItemWidgetStyle { static const EdgeInsetsGeometry padding = EdgeInsetsDirectional.only(start: 4); static const EdgeInsetsGeometry counterPadding = EdgeInsetsDirectional.symmetric(vertical: 5, horizontal: 8); + static const EdgeInsetsGeometry mobileCounterPadding = EdgeInsetsDirectional.symmetric(vertical: 8, horizontal: 8); static const EdgeInsetsGeometry counterMargin = EdgeInsetsDirectional.only(top: PlatformInfo.isWeb ? 8 : 0); static const TextStyle labelTextStyle = TextStyle( diff --git a/lib/features/composer/presentation/styles/attachment_composer_widget_style.dart b/lib/features/composer/presentation/styles/web/attachment_composer_widget_style.dart similarity index 100% rename from lib/features/composer/presentation/styles/attachment_composer_widget_style.dart rename to lib/features/composer/presentation/styles/web/attachment_composer_widget_style.dart diff --git a/lib/features/composer/presentation/styles/bottom_bar_composer_widget_style.dart b/lib/features/composer/presentation/styles/web/bottom_bar_composer_widget_style.dart similarity index 100% rename from lib/features/composer/presentation/styles/bottom_bar_composer_widget_style.dart rename to lib/features/composer/presentation/styles/web/bottom_bar_composer_widget_style.dart diff --git a/lib/features/composer/presentation/styles/desktop_container_view_style.dart b/lib/features/composer/presentation/styles/web/desktop_responsive_container_view_style.dart similarity index 85% rename from lib/features/composer/presentation/styles/desktop_container_view_style.dart rename to lib/features/composer/presentation/styles/web/desktop_responsive_container_view_style.dart index 1ecc1a4ca..c0bec80ee 100644 --- a/lib/features/composer/presentation/styles/desktop_container_view_style.dart +++ b/lib/features/composer/presentation/styles/web/desktop_responsive_container_view_style.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -class DesktopContainerViewStyle { +class DesktopResponsiveContainerViewStyle { static const double radius = 28; static const double margin = 20; static const double elevation = 16; diff --git a/lib/features/composer/presentation/styles/mobile_container_view_style.dart b/lib/features/composer/presentation/styles/web/mobile_responsive_container_view_style.dart similarity index 80% rename from lib/features/composer/presentation/styles/mobile_container_view_style.dart rename to lib/features/composer/presentation/styles/web/mobile_responsive_container_view_style.dart index 928a72dad..fcca155ec 100644 --- a/lib/features/composer/presentation/styles/mobile_container_view_style.dart +++ b/lib/features/composer/presentation/styles/web/mobile_responsive_container_view_style.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -class MobileContainerViewStyle { +class MobileResponsiveContainerViewStyle { static const double radius = 28; static const double elevation = 16; diff --git a/lib/features/composer/presentation/styles/tablet_container_view_style.dart b/lib/features/composer/presentation/styles/web/tablet_responsive_container_view_style.dart similarity index 95% rename from lib/features/composer/presentation/styles/tablet_container_view_style.dart rename to lib/features/composer/presentation/styles/web/tablet_responsive_container_view_style.dart index 3421c420e..8f7645d7a 100644 --- a/lib/features/composer/presentation/styles/tablet_container_view_style.dart +++ b/lib/features/composer/presentation/styles/web/tablet_responsive_container_view_style.dart @@ -2,7 +2,7 @@ import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:flutter/material.dart'; -class TabletContainerViewStyle { +class TabletResponsiveContainerViewStyle { static const double radius = 28; static const double elevation = 16; diff --git a/lib/features/composer/presentation/view/editor_view_mixin.dart b/lib/features/composer/presentation/view/editor_view_mixin.dart new file mode 100644 index 000000000..8e54f1cb0 --- /dev/null +++ b/lib/features/composer/presentation/view/editor_view_mixin.dart @@ -0,0 +1,27 @@ + +import 'package:core/presentation/extensions/html_extension.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:flutter/material.dart'; +import 'package:model/email/email_action_type.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart'; + +mixin EditorViewMixin { + String getEmailContentQuotedAsHtml({ + required BuildContext context, + required String emailContent, + required EmailActionType emailActionType, + required PresentationEmail presentationEmail, + }) { + final headerEmailQuoted = emailActionType.getHeaderEmailQuoted( + context: context, + presentationEmail: presentationEmail + ); + log('EditorViewMixin::getEmailContentQuotedAsHtml:headerEmailQuoted: $headerEmailQuoted'); + final headerEmailQuotedAsHtml = headerEmailQuoted != null + ? headerEmailQuoted.addCiteTag() + : ''; + final emailQuotedHtml = '${HtmlExtension.editorStartTags}$headerEmailQuotedAsHtml${emailContent.addBlockQuoteTag()}'; + return emailQuotedHtml; + } +} \ No newline at end of file diff --git a/lib/features/composer/presentation/view/desktop_container_view.dart b/lib/features/composer/presentation/view/web/desktop_responsive_container_view.dart similarity index 76% rename from lib/features/composer/presentation/view/desktop_container_view.dart rename to lib/features/composer/presentation/view/web/desktop_responsive_container_view.dart index f0367cfa3..4b32e2d5f 100644 --- a/lib/features/composer/presentation/view/desktop_container_view.dart +++ b/lib/features/composer/presentation/view/web/desktop_responsive_container_view.dart @@ -3,10 +3,10 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; -import 'package:tmail_ui_user/features/composer/presentation/styles/desktop_container_view_style.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/minimize_composer_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/styles/web/desktop_responsive_container_view_style.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/web/minimize_composer_widget.dart'; -class DesktopContainerView extends StatelessWidget { +class DesktopResponsiveContainerView extends StatelessWidget { final ScreenDisplayMode displayMode; final String emailSubject; @@ -16,8 +16,8 @@ class DesktopContainerView extends StatelessWidget { final _responsiveUtils = Get.find(); - DesktopContainerView({ - super.key, + DesktopResponsiveContainerView({ + super.key, required this.childBuilder, required this.displayMode, required this.emailSubject, @@ -29,8 +29,8 @@ class DesktopContainerView extends StatelessWidget { Widget build(BuildContext context) { if (displayMode == ScreenDisplayMode.minimize) { return PositionedDirectional( - end: DesktopContainerViewStyle.margin, - bottom: DesktopContainerViewStyle.margin, + end: DesktopResponsiveContainerViewStyle.margin, + bottom: DesktopResponsiveContainerViewStyle.margin, child: PointerInterceptor( child: MinimizeComposerWidget( emailSubject: emailSubject, @@ -44,16 +44,16 @@ class DesktopContainerView extends StatelessWidget { final maxHeight = _responsiveUtils.getSizeScreenHeight(context) * 0.75; return PositionedDirectional( - end: DesktopContainerViewStyle.margin, - bottom: DesktopContainerViewStyle.margin, + end: DesktopResponsiveContainerViewStyle.margin, + bottom: DesktopResponsiveContainerViewStyle.margin, child: Card( - elevation: DesktopContainerViewStyle.elevation, + elevation: DesktopResponsiveContainerViewStyle.elevation, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(DesktopContainerViewStyle.radius)) + borderRadius: BorderRadius.all(Radius.circular(DesktopResponsiveContainerViewStyle.radius)) ), clipBehavior: Clip.antiAlias, child: Container( - color: DesktopContainerViewStyle.backgroundColor, + color: DesktopResponsiveContainerViewStyle.backgroundColor, width: maxWidth, height: maxHeight, child: LayoutBuilder(builder: (context, constraints) => @@ -69,16 +69,16 @@ class DesktopContainerView extends StatelessWidget { final maxHeight = _responsiveUtils.getSizeScreenHeight(context) * 0.9; return Scaffold( - backgroundColor: DesktopContainerViewStyle.outSideBackgroundColor, + backgroundColor: DesktopResponsiveContainerViewStyle.outSideBackgroundColor, body: Center( child: Card( - elevation: DesktopContainerViewStyle.elevation, + elevation: DesktopResponsiveContainerViewStyle.elevation, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(DesktopContainerViewStyle.radius)) + borderRadius: BorderRadius.all(Radius.circular(DesktopResponsiveContainerViewStyle.radius)) ), clipBehavior: Clip.antiAlias, child: Container( - color: DesktopContainerViewStyle.backgroundColor, + color: DesktopResponsiveContainerViewStyle.backgroundColor, width: maxWidth, height: maxHeight, child: LayoutBuilder(builder: (context, constraints) => diff --git a/lib/features/composer/presentation/view/mobile_container_view.dart b/lib/features/composer/presentation/view/web/mobile_responsive_container_view.dart similarity index 70% rename from lib/features/composer/presentation/view/mobile_container_view.dart rename to lib/features/composer/presentation/view/web/mobile_responsive_container_view.dart index 07671d40c..0efbe8513 100644 --- a/lib/features/composer/presentation/view/mobile_container_view.dart +++ b/lib/features/composer/presentation/view/web/mobile_responsive_container_view.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; -import 'package:tmail_ui_user/features/composer/presentation/styles/mobile_container_view_style.dart'; +import 'package:tmail_ui_user/features/composer/presentation/styles/web/mobile_responsive_container_view_style.dart'; -class MobileContainerView extends StatelessWidget { +class MobileResponsiveContainerView extends StatelessWidget { final Widget Function(BuildContext context, BoxConstraints constraints) childBuilder; - const MobileContainerView({ + const MobileResponsiveContainerView({ super.key, required this.childBuilder, }); @@ -14,7 +14,7 @@ class MobileContainerView extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: MobileContainerViewStyle.outSideBackgroundColor, + backgroundColor: MobileResponsiveContainerViewStyle.outSideBackgroundColor, body: LayoutBuilder(builder: (context, constraints) => PointerInterceptor( child: childBuilder.call(context, constraints) diff --git a/lib/features/composer/presentation/view/tablet_container_view.dart b/lib/features/composer/presentation/view/web/tablet_responsive_container_view.dart similarity index 63% rename from lib/features/composer/presentation/view/tablet_container_view.dart rename to lib/features/composer/presentation/view/web/tablet_responsive_container_view.dart index 27ce6e54b..1263612d7 100644 --- a/lib/features/composer/presentation/view/tablet_container_view.dart +++ b/lib/features/composer/presentation/view/web/tablet_responsive_container_view.dart @@ -2,15 +2,15 @@ import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; -import 'package:tmail_ui_user/features/composer/presentation/styles/tablet_container_view_style.dart'; +import 'package:tmail_ui_user/features/composer/presentation/styles/web/tablet_responsive_container_view_style.dart'; -class TabletContainerView extends StatelessWidget { +class TabletResponsiveContainerView extends StatelessWidget { final Widget Function(BuildContext context, BoxConstraints constraints) childBuilder; final _responsiveUtils = Get.find(); - TabletContainerView({ + TabletResponsiveContainerView({ super.key, required this.childBuilder, }); @@ -18,18 +18,18 @@ class TabletContainerView extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: TabletContainerViewStyle.outSideBackgroundColor, + backgroundColor: TabletResponsiveContainerViewStyle.outSideBackgroundColor, body: Center( child: Card( - elevation: TabletContainerViewStyle.elevation, - margin: TabletContainerViewStyle.getMargin(context, _responsiveUtils), + elevation: TabletResponsiveContainerViewStyle.elevation, + margin: TabletResponsiveContainerViewStyle.getMargin(context, _responsiveUtils), shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(TabletContainerViewStyle.radius)) + borderRadius: BorderRadius.all(Radius.circular(TabletResponsiveContainerViewStyle.radius)) ), clipBehavior: Clip.antiAlias, child: Container( - color: TabletContainerViewStyle.backgroundColor, - width: TabletContainerViewStyle.getWidth(context, _responsiveUtils), + color: TabletResponsiveContainerViewStyle.backgroundColor, + width: TabletResponsiveContainerViewStyle.getWidth(context, _responsiveUtils), child: LayoutBuilder(builder: (context, constraints) => PointerInterceptor( child: childBuilder.call(context, constraints) diff --git a/lib/features/composer/presentation/view/web_editor_view.dart b/lib/features/composer/presentation/view/web/web_editor_view.dart similarity index 85% rename from lib/features/composer/presentation/view/web_editor_view.dart rename to lib/features/composer/presentation/view/web/web_editor_view.dart index c8a436ae0..94bf1222d 100644 --- a/lib/features/composer/presentation/view/web_editor_view.dart +++ b/lib/features/composer/presentation/view/web/web_editor_view.dart @@ -2,21 +2,19 @@ import 'package:core/presentation/extensions/html_extension.dart'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; -import 'package:core/utils/app_logger.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:html_editor_enhanced/html_editor.dart'; import 'package:model/email/email_action_type.dart'; -import 'package:model/email/presentation_email.dart'; import 'package:tmail_ui_user/features/base/widget/cupertino_loading_widget.dart'; -import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/web_editor_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/view/editor_view_mixin.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/web/web_editor_widget.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/transform_html_email_content_state.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart'; -class WebEditorView extends StatelessWidget { +class WebEditorView extends StatelessWidget with EditorViewMixin { final HtmlEditorController editorController; final ComposerArguments? arguments; @@ -115,7 +113,7 @@ class WebEditorView extends StatelessWidget { } return contentViewState!.fold( (failure) { - final emailContentQuoted = _getEmailContentQuotedAsHtml( + final emailContentQuoted = getEmailContentQuotedAsHtml( context: context, emailContent: '', emailActionType: arguments!.emailActionType, @@ -137,7 +135,7 @@ class WebEditorView extends StatelessWidget { if (success is TransformHtmlEmailContentLoading) { return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0)); } else { - final emailContentQuoted = _getEmailContentQuotedAsHtml( + final emailContentQuoted = getEmailContentQuotedAsHtml( context: context, emailContent: success is TransformHtmlEmailContentSuccess ? success.htmlContent @@ -173,22 +171,4 @@ class WebEditorView extends StatelessWidget { ); } } - - String _getEmailContentQuotedAsHtml({ - required BuildContext context, - required String emailContent, - required EmailActionType emailActionType, - required PresentationEmail presentationEmail, - }) { - final headerEmailQuoted = emailActionType.getHeaderEmailQuoted( - context: context, - presentationEmail: presentationEmail - ); - log('WebEditorView::getEmailContentQuotedAsHtml:headerEmailQuoted: $headerEmailQuoted'); - final headerEmailQuotedAsHtml = headerEmailQuoted != null - ? headerEmailQuoted.addCiteTag() - : ''; - final emailQuotedHtml = '${HtmlExtension.editorStartTags}$headerEmailQuotedAsHtml${emailContent.addBlockQuoteTag()}'; - return emailQuotedHtml; - } -} +} \ No newline at end of file diff --git a/lib/features/composer/presentation/widgets/mobile/app_bar_composer_widget.dart b/lib/features/composer/presentation/widgets/mobile/app_bar_composer_widget.dart new file mode 100644 index 000000000..69821985e --- /dev/null +++ b/lib/features/composer/presentation/widgets/mobile/app_bar_composer_widget.dart @@ -0,0 +1,68 @@ +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/composer/presentation/styles/mobile_app_bar_composer_widget_style.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class AppBarComposerWidget extends StatelessWidget { + + final bool isSendButtonEnabled; + final VoidCallback onCloseViewAction; + final VoidCallback sendMessageAction; + final VoidCallback openContextMenuAction; + + final _imagePaths = Get.find(); + + AppBarComposerWidget({ + super.key, + required this.isSendButtonEnabled, + required this.onCloseViewAction, + required this.sendMessageAction, + required this.openContextMenuAction, + }); + + @override + Widget build(BuildContext context) { + return Container( + height: MobileAppBarComposerWidgetStyle.height, + color: MobileAppBarComposerWidgetStyle.backgroundColor, + padding: MobileAppBarComposerWidgetStyle.padding, + child: Row( + children: [ + TMailButtonWidget.fromIcon( + icon: _imagePaths.icCancel, + backgroundColor: Colors.transparent, + tooltipMessage: AppLocalizations.of(context).saveAndClose, + iconSize: MobileAppBarComposerWidgetStyle.iconSize, + iconColor: MobileAppBarComposerWidgetStyle.iconColor, + padding: MobileAppBarComposerWidgetStyle.iconPadding, + onTapActionCallback: onCloseViewAction + ), + const Spacer(), + TMailButtonWidget.fromIcon( + icon: isSendButtonEnabled + ? _imagePaths.icSendMobile + : _imagePaths.icSendDisable, + backgroundColor: Colors.transparent, + padding: MobileAppBarComposerWidgetStyle.iconPadding, + iconSize: MobileAppBarComposerWidgetStyle.sendButtonIconSize, + tooltipMessage: AppLocalizations.of(context).send, + onTapActionCallback: sendMessageAction, + ), + const SizedBox(width: MobileAppBarComposerWidgetStyle.space), + TMailButtonWidget.fromIcon( + icon: _imagePaths.icMore, + iconColor: MobileAppBarComposerWidgetStyle.iconColor, + borderRadius: MobileAppBarComposerWidgetStyle.iconRadius, + backgroundColor: Colors.transparent, + padding: MobileAppBarComposerWidgetStyle.iconPadding, + iconSize: MobileAppBarComposerWidgetStyle.iconSize, + tooltipMessage: AppLocalizations.of(context).more, + onTapActionCallback: openContextMenuAction, + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/composer/presentation/widgets/mobile/landscape_app_bar_composer_widget.dart b/lib/features/composer/presentation/widgets/mobile/landscape_app_bar_composer_widget.dart new file mode 100644 index 000000000..1a0acc47a --- /dev/null +++ b/lib/features/composer/presentation/widgets/mobile/landscape_app_bar_composer_widget.dart @@ -0,0 +1,72 @@ +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/composer/presentation/styles/mobile_app_bar_composer_widget_style.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class LandscapeAppBarComposerWidget extends StatelessWidget { + + final bool isSendButtonEnabled; + final VoidCallback onCloseViewAction; + final VoidCallback sendMessageAction; + final VoidCallback openContextMenuAction; + + final _imagePaths = Get.find(); + + LandscapeAppBarComposerWidget({ + super.key, + required this.isSendButtonEnabled, + required this.onCloseViewAction, + required this.sendMessageAction, + required this.openContextMenuAction, + }); + + @override + Widget build(BuildContext context) { + return Container( + height: MobileAppBarComposerWidgetStyle.height, + color: MobileAppBarComposerWidgetStyle.backgroundColor, + padding: MobileAppBarComposerWidgetStyle.padding, + child: SafeArea( + top: false, + bottom: false, + child: Row( + children: [ + TMailButtonWidget.fromIcon( + icon: _imagePaths.icCancel, + backgroundColor: Colors.transparent, + tooltipMessage: AppLocalizations.of(context).saveAndClose, + iconSize: MobileAppBarComposerWidgetStyle.iconSize, + iconColor: MobileAppBarComposerWidgetStyle.iconColor, + padding: MobileAppBarComposerWidgetStyle.iconPadding, + onTapActionCallback: onCloseViewAction + ), + const Spacer(), + TMailButtonWidget.fromIcon( + icon: isSendButtonEnabled + ? _imagePaths.icSendMobile + : _imagePaths.icSendDisable, + backgroundColor: Colors.transparent, + padding: MobileAppBarComposerWidgetStyle.iconPadding, + iconSize: MobileAppBarComposerWidgetStyle.sendButtonIconSize, + tooltipMessage: AppLocalizations.of(context).send, + onTapActionCallback: sendMessageAction, + ), + const SizedBox(width: MobileAppBarComposerWidgetStyle.space), + TMailButtonWidget.fromIcon( + icon: _imagePaths.icMore, + iconColor: MobileAppBarComposerWidgetStyle.iconColor, + borderRadius: MobileAppBarComposerWidgetStyle.iconRadius, + backgroundColor: Colors.transparent, + padding: MobileAppBarComposerWidgetStyle.iconPadding, + iconSize: MobileAppBarComposerWidgetStyle.iconSize, + tooltipMessage: AppLocalizations.of(context).more, + onTapActionCallback: openContextMenuAction, + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/composer/presentation/widgets/mobile/mobile_attachment_composer_widget.dart b/lib/features/composer/presentation/widgets/mobile/mobile_attachment_composer_widget.dart new file mode 100644 index 000000000..cc6364709 --- /dev/null +++ b/lib/features/composer/presentation/widgets/mobile/mobile_attachment_composer_widget.dart @@ -0,0 +1,86 @@ +import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:tmail_ui_user/features/composer/presentation/styles/mobile/mobile_attachment_composer_widget_style.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_item_composer_widget.dart'; +import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +typedef OnShowMoreAttachmentAction = void Function(bool isShowMore); + +class MobileAttachmentComposerWidget extends StatefulWidget { + + final List listFileUploaded; + final bool isShowMore; + final OnDeleteAttachmentAction onDeleteAttachmentAction; + final OnShowMoreAttachmentAction onShowMoreAttachmentAction; + + const MobileAttachmentComposerWidget({ + super.key, + required this.listFileUploaded, + required this.isShowMore, + required this.onDeleteAttachmentAction, + required this.onShowMoreAttachmentAction, + }); + + @override + State createState() => _MobileAttachmentComposerWidgetState(); +} + +class _MobileAttachmentComposerWidgetState extends State { + + static const int _maxDisplayedItem = 6; + + bool _isShowMore = false; + int _currentCountDisplayed = _maxDisplayedItem; + + @override + void initState() { + super.initState(); + _isShowMore = widget.isShowMore; + if (_isShowMore) { + _currentCountDisplayed = widget.listFileUploaded.length; + } else { + _currentCountDisplayed = widget.listFileUploaded.length > 6 + ? 6 + : widget.listFileUploaded.length; + } + } + + @override + Widget build(BuildContext context) { + final displayedFiles = widget.listFileUploaded.sublist(0, _currentCountDisplayed); + + return Container( + color: MobileAttachmentComposerWidgetStyle.backgroundColor, + width: double.infinity, + padding: MobileAttachmentComposerWidgetStyle.padding, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + spacing: MobileAttachmentComposerWidgetStyle.listItemSpace, + runSpacing: MobileAttachmentComposerWidgetStyle.listItemSpace, + children: displayedFiles + .map((file) => AttachmentItemComposerWidget( + fileState: file, + onDeleteAttachmentAction: widget.onDeleteAttachmentAction + )) + .toList(), + ), + if (!_isShowMore) + TMailButtonWidget.fromText( + text: AppLocalizations.of(context).showMoreAttachment(widget.listFileUploaded.length), + textStyle: MobileAttachmentComposerWidgetStyle.showMoreButtonTextStyle, + margin: MobileAttachmentComposerWidgetStyle.showMoreMargin, + backgroundColor: Colors.transparent, + onTapActionCallback: () { + setState(() => _isShowMore = true); + widget.onShowMoreAttachmentAction(_isShowMore); + }, + ) + ] + ), + ); + } +} diff --git a/lib/features/composer/presentation/widgets/mobile/tablet_bottom_bar_composer_widget.dart b/lib/features/composer/presentation/widgets/mobile/tablet_bottom_bar_composer_widget.dart new file mode 100644 index 000000000..1ce88a7a8 --- /dev/null +++ b/lib/features/composer/presentation/widgets/mobile/tablet_bottom_bar_composer_widget.dart @@ -0,0 +1,65 @@ +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/composer/presentation/styles/mobile/tablet_bottom_bar_composer_widget_style.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class TabletBottomBarComposerWidget extends StatelessWidget { + + final VoidCallback deleteComposerAction; + final VoidCallback saveToDraftAction; + final VoidCallback sendMessageAction; + + final _imagePaths = Get.find(); + + TabletBottomBarComposerWidget({ + super.key, + required this.deleteComposerAction, + required this.saveToDraftAction, + required this.sendMessageAction, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: TabletBottomBarComposerWidgetStyle.padding, + color: TabletBottomBarComposerWidgetStyle.backgroundColor, + child: Row( + children: [ + const Spacer(), + TMailButtonWidget.fromIcon( + icon: _imagePaths.icDeleteMailbox, + borderRadius: TabletBottomBarComposerWidgetStyle.iconRadius, + padding: TabletBottomBarComposerWidgetStyle.iconPadding, + iconSize: TabletBottomBarComposerWidgetStyle.iconSize, + tooltipMessage: AppLocalizations.of(context).delete, + onTapActionCallback: deleteComposerAction, + ), + const SizedBox(width: TabletBottomBarComposerWidgetStyle.space), + TMailButtonWidget.fromIcon( + icon: _imagePaths.icSaveToDraft, + borderRadius: TabletBottomBarComposerWidgetStyle.iconRadius, + padding: TabletBottomBarComposerWidgetStyle.iconPadding, + iconSize: TabletBottomBarComposerWidgetStyle.iconSize, + tooltipMessage: AppLocalizations.of(context).save_to_drafts, + onTapActionCallback: saveToDraftAction, + ), + const SizedBox(width: TabletBottomBarComposerWidgetStyle.sendButtonSpace), + TMailButtonWidget( + text: AppLocalizations.of(context).send, + icon: _imagePaths.icSend, + iconAlignment: TextDirection.rtl, + padding: TabletBottomBarComposerWidgetStyle.sendButtonPadding, + iconSize: TabletBottomBarComposerWidgetStyle.iconSize, + iconSpace: TabletBottomBarComposerWidgetStyle.sendButtonIconSpace, + textStyle: TabletBottomBarComposerWidgetStyle.sendButtonTextStyle, + backgroundColor: TabletBottomBarComposerWidgetStyle.sendButtonBackgroundColor, + borderRadius: TabletBottomBarComposerWidgetStyle.sendButtonRadius, + onTapActionCallback: sendMessageAction, + ) + ] + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/composer/presentation/widgets/recipient_tag_item_widget.dart b/lib/features/composer/presentation/widgets/recipient_tag_item_widget.dart index ee532c67b..885591a88 100644 --- a/lib/features/composer/presentation/widgets/recipient_tag_item_widget.dart +++ b/lib/features/composer/presentation/widgets/recipient_tag_item_widget.dart @@ -92,7 +92,9 @@ class RecipientTagItemWidget extends StatelessWidget { onTapActionCallback: () => onShowFullAction?.call(prefix), borderRadius: RecipientTagItemWidgetStyle.radius, textStyle: RecipientTagItemWidgetStyle.labelTextStyle, - padding: RecipientTagItemWidgetStyle.counterPadding, + padding: PlatformInfo.isWeb + ? RecipientTagItemWidgetStyle.counterPadding + : RecipientTagItemWidgetStyle.mobileCounterPadding, backgroundColor: AppColor.colorEmailAddressTag, ) ] diff --git a/lib/features/composer/presentation/widgets/attachment_composer_widget.dart b/lib/features/composer/presentation/widgets/web/attachment_composer_widget.dart similarity index 98% rename from lib/features/composer/presentation/widgets/attachment_composer_widget.dart rename to lib/features/composer/presentation/widgets/web/attachment_composer_widget.dart index bdc5ae089..9f18d17f1 100644 --- a/lib/features/composer/presentation/widgets/attachment_composer_widget.dart +++ b/lib/features/composer/presentation/widgets/web/attachment_composer_widget.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:tmail_ui_user/features/composer/presentation/styles/attachment_composer_widget_style.dart'; +import 'package:tmail_ui_user/features/composer/presentation/styles/web/attachment_composer_widget_style.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_item_composer_widget.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_header_composer_widget.dart'; import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart'; diff --git a/lib/features/composer/presentation/widgets/bottom_bar_composer_widget.dart b/lib/features/composer/presentation/widgets/web/bottom_bar_composer_widget.dart similarity index 99% rename from lib/features/composer/presentation/widgets/bottom_bar_composer_widget.dart rename to lib/features/composer/presentation/widgets/web/bottom_bar_composer_widget.dart index 9d8d98a4f..645b5cf76 100644 --- a/lib/features/composer/presentation/widgets/bottom_bar_composer_widget.dart +++ b/lib/features/composer/presentation/widgets/web/bottom_bar_composer_widget.dart @@ -2,7 +2,7 @@ import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:tmail_ui_user/features/composer/presentation/styles/bottom_bar_composer_widget_style.dart'; +import 'package:tmail_ui_user/features/composer/presentation/styles/web/bottom_bar_composer_widget_style.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class BottomBarComposerWidget extends StatelessWidget { diff --git a/lib/features/composer/presentation/widgets/app_bar_composer_widget.dart b/lib/features/composer/presentation/widgets/web/desktop_app_bar_composer_widget.dart similarity index 97% rename from lib/features/composer/presentation/widgets/app_bar_composer_widget.dart rename to lib/features/composer/presentation/widgets/web/desktop_app_bar_composer_widget.dart index c71bcf978..ae7cc28f3 100644 --- a/lib/features/composer/presentation/widgets/app_bar_composer_widget.dart +++ b/lib/features/composer/presentation/widgets/web/desktop_app_bar_composer_widget.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; import 'package:tmail_ui_user/features/composer/presentation/styles/app_bar_composer_widget_style.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/minimize_composer_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/web/minimize_composer_widget.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/title_composer_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -class AppBarComposerWidget extends StatelessWidget { +class DesktopAppBarComposerWidget extends StatelessWidget { final String emailSubject; final VoidCallback onCloseViewAction; @@ -18,7 +18,7 @@ class AppBarComposerWidget extends StatelessWidget { final _imagePaths = Get.find(); - AppBarComposerWidget({ + DesktopAppBarComposerWidget({ super.key, required this.emailSubject, required this.onCloseViewAction, diff --git a/lib/features/composer/presentation/widgets/minimize_composer_widget.dart b/lib/features/composer/presentation/widgets/web/minimize_composer_widget.dart similarity index 100% rename from lib/features/composer/presentation/widgets/minimize_composer_widget.dart rename to lib/features/composer/presentation/widgets/web/minimize_composer_widget.dart diff --git a/lib/features/composer/presentation/widgets/mobile_app_bar_composer_widget.dart b/lib/features/composer/presentation/widgets/web/mobile_responsive_app_bar_composer_widget.dart similarity index 97% rename from lib/features/composer/presentation/widgets/mobile_app_bar_composer_widget.dart rename to lib/features/composer/presentation/widgets/web/mobile_responsive_app_bar_composer_widget.dart index af2054b16..e2b8e4aa2 100644 --- a/lib/features/composer/presentation/widgets/mobile_app_bar_composer_widget.dart +++ b/lib/features/composer/presentation/widgets/web/mobile_responsive_app_bar_composer_widget.dart @@ -5,7 +5,7 @@ import 'package:get/get.dart'; import 'package:tmail_ui_user/features/composer/presentation/styles/mobile_app_bar_composer_widget_style.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -class MobileAppBarComposerWidget extends StatelessWidget { +class MobileResponsiveAppBarComposerWidget extends StatelessWidget { final bool isCodeViewEnabled; final bool isSendButtonEnabled; @@ -19,7 +19,7 @@ class MobileAppBarComposerWidget extends StatelessWidget { final _imagePaths = Get.find(); - MobileAppBarComposerWidget({ + MobileResponsiveAppBarComposerWidget({ super.key, required this.isCodeViewEnabled, required this.isFormattingOptionsEnabled, diff --git a/lib/features/composer/presentation/widgets/toolbar_rich_text_builder.dart b/lib/features/composer/presentation/widgets/web/toolbar_rich_text_builder.dart similarity index 100% rename from lib/features/composer/presentation/widgets/toolbar_rich_text_builder.dart rename to lib/features/composer/presentation/widgets/web/toolbar_rich_text_builder.dart diff --git a/lib/features/composer/presentation/widgets/web_editor_widget.dart b/lib/features/composer/presentation/widgets/web/web_editor_widget.dart similarity index 98% rename from lib/features/composer/presentation/widgets/web_editor_widget.dart rename to lib/features/composer/presentation/widgets/web/web_editor_widget.dart index 03754c6e0..b1e2a1165 100644 --- a/lib/features/composer/presentation/widgets/web_editor_widget.dart +++ b/lib/features/composer/presentation/widgets/web/web_editor_widget.dart @@ -60,7 +60,7 @@ class _WebEditorState extends State { @override Widget build(BuildContext context) { return HtmlEditor( - key: const Key('composer_editor_web'), + key: const Key('web_editor'), controller: _editorController, htmlEditorOptions: HtmlEditorOptions( shouldEnsureVisible: true, diff --git a/lib/features/identity_creator/presentation/identity_creator_view.dart b/lib/features/identity_creator/presentation/identity_creator_view.dart index f19504716..8711433bb 100644 --- a/lib/features/identity_creator/presentation/identity_creator_view.dart +++ b/lib/features/identity_creator/presentation/identity_creator_view.dart @@ -18,7 +18,7 @@ import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:rich_text_composer/rich_text_composer.dart'; import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart'; import 'package:tmail_ui_user/features/composer/presentation/mixin/rich_text_button_mixin.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/toolbar_rich_text_builder.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/web/toolbar_rich_text_builder.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/identity_creator_controller.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/widgets/identity_drop_list_field_builder.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/widgets/identity_field_no_editable_builder.dart'; diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index e096e96a3..b900184c0 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-09-06T11:36:27.238808", + "@@last_modified": "2023-09-07T09:30:18.682623", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -3223,5 +3223,15 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "showMoreAttachment": "Show more (+{count})", + "@showMoreAttachment": { + "type": "text", + "placeholders_order": [ + "count" + ], + "placeholders": { + "count": {} + } } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 6a4807636..ff861b853 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -3325,4 +3325,12 @@ class AppLocalizations { name: 'embedCode' ); } + + String showMoreAttachment(int count) { + return Intl.message( + 'Show more (+$count)', + name: 'showMoreAttachment', + args: [count] + ); + } } \ No newline at end of file