diff --git a/core/pubspec.yaml b/core/pubspec.yaml index 376160483..d4e4551df 100644 --- a/core/pubspec.yaml +++ b/core/pubspec.yaml @@ -55,7 +55,7 @@ dependencies: pointer_interceptor: 0.9.1 - flutter_keyboard_visibility: 5.4.0 + flutter_keyboard_visibility: 5.4.1 flex_color_picker: 3.2.0 @@ -85,10 +85,6 @@ dev_dependencies: # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec -dependency_overrides: - - flutter_keyboard_visibility: 5.4.1 - flutter: # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in diff --git a/lib/features/base/widget/compose_floating_button.dart b/lib/features/base/widget/compose_floating_button.dart index 517c4b10b..6d1d3c9d6 100644 --- a/lib/features/base/widget/compose_floating_button.dart +++ b/lib/features/base/widget/compose_floating_button.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; -import 'package:tmail_ui_user/main/utils/app_utils.dart'; class ComposeFloatingButton extends StatelessWidget { @@ -25,9 +24,7 @@ class ComposeFloatingButton extends StatelessWidget { final imagePaths = getBinding(); return Align( - alignment: AppUtils.isDirectionRTL(context) - ? Alignment.bottomLeft - : Alignment.bottomRight, + alignment: AlignmentDirectional.bottomEnd, child: ScrollingFloatingButtonAnimated( icon: SvgPicture.asset( imagePaths!.icComposeWeb, @@ -36,10 +33,7 @@ class ComposeFloatingButton extends StatelessWidget { fit: BoxFit.fill ), text: Padding( - padding: EdgeInsets.only( - right: AppUtils.isDirectionRTL(context) ? 0 : 16, - left: AppUtils.isDirectionRTL(context) ? 16 : 0, - ), + padding: const EdgeInsetsDirectional.only(end: 16), child: Text(AppLocalizations.of(context).compose, overflow: CommonTextStyle.defaultTextOverFlow, softWrap: CommonTextStyle.defaultSoftWrap, diff --git a/lib/features/composer/presentation/base_composer_view.dart b/lib/features/composer/presentation/base_composer_view.dart new file mode 100644 index 000000000..fb8e3036c --- /dev/null +++ b/lib/features/composer/presentation/base_composer_view.dart @@ -0,0 +1,550 @@ +import 'package:core/presentation/extensions/capitalize_extension.dart'; +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/extensions/string_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/list/sliver_grid_delegate_fixed_height.dart'; +import 'package:core/presentation/views/text/text_field_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/styles/composer_style.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.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: Text( + controller.identitySelected.value != null + ? (controller.identitySelected.value?.email ?? '').withUnicodeCharacter + : (controller.userProfile?.email ?? '').withUnicodeCharacter, + maxLines: 1, + overflow: CommonTextStyle.defaultTextOverFlow, + softWrap: CommonTextStyle.defaultSoftWrap, + 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 Column( + children: [ + Obx(() { + return (EmailAddressInputBuilder( + context, + imagePaths, + responsiveUtils, + PrefixEmailAddress.to, + controller.listToEmailAddress, + controller.listEmailAddressType, + expandMode: controller.toAddressExpandMode.value, + controller: controller.toEmailAddressController, + focusNode: controller.toAddressFocusNode, + autoDisposeFocusNode: false, + keyTagEditor: controller.keyToEmailTagEditor, + isInitial: controller.isInitialRecipient.value, + nextFocusNode: controller.getNextFocusOfToEmailAddress() + ) + ..addOnFocusEmailAddressChangeAction(controller.onEmailAddressFocusChange) + ..addOnShowFullListEmailAddressAction(controller.showFullEmailAddress) + ..addOnAddEmailAddressTypeAction(controller.addEmailAddressType) + ..addOnUpdateListEmailAddressAction(controller.updateListEmailAddress) + ..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion) + ..addOnFocusNextAddressAction(controller.handleFocusNextAddressAction) + ).build(); + }), + Obx(() { + if (controller.listEmailAddressType.contains(PrefixEmailAddress.cc) == true) { + return const Divider(color: AppColor.colorDividerComposer, height: 1); + } else { + return const SizedBox.shrink(); + } + }), + Obx(() { + if (controller.listEmailAddressType.contains(PrefixEmailAddress.cc) == true) { + return (EmailAddressInputBuilder( + context, + imagePaths, + responsiveUtils, + PrefixEmailAddress.cc, + controller.listCcEmailAddress, + controller.listEmailAddressType, + focusNode: controller.ccAddressFocusNode, + expandMode: controller.ccAddressExpandMode.value, + controller: controller.ccEmailAddressController, + keyTagEditor: controller.keyCcEmailTagEditor, + autoDisposeFocusNode: false, + isInitial: controller.isInitialRecipient.value, + nextFocusNode: controller.getNextFocusOfCcEmailAddress() + ) + ..addOnFocusEmailAddressChangeAction(controller.onEmailAddressFocusChange) + ..addOnShowFullListEmailAddressAction(controller.showFullEmailAddress) + ..addOnDeleteEmailAddressTypeAction(controller.deleteEmailAddressType) + ..addOnUpdateListEmailAddressAction(controller.updateListEmailAddress) + ..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion) + ..addOnFocusNextAddressAction(controller.handleFocusNextAddressAction) + ).build(); + } else { + return const SizedBox.shrink(); + } + }), + Obx(() { + if (controller.listEmailAddressType.contains(PrefixEmailAddress.bcc) == true) { + return const Divider(color: AppColor.colorDividerComposer, height: 1); + } else { + return const SizedBox.shrink(); + } + }), + Obx(() { + if (controller.listEmailAddressType.contains(PrefixEmailAddress.bcc) == true) { + return (EmailAddressInputBuilder( + context, + imagePaths, + responsiveUtils, + PrefixEmailAddress.bcc, + controller.listBccEmailAddress, + controller.listEmailAddressType, + focusNode: controller.bccAddressFocusNode, + expandMode: controller.bccAddressExpandMode.value, + controller: controller.bccEmailAddressController, + autoDisposeFocusNode: false, + keyTagEditor: controller.keyBccEmailTagEditor, + isInitial: controller.isInitialRecipient.value, + nextFocusNode: controller.subjectEmailInputFocusNode + ) + ..addOnFocusEmailAddressChangeAction(controller.onEmailAddressFocusChange) + ..addOnShowFullListEmailAddressAction(controller.showFullEmailAddress) + ..addOnDeleteEmailAddressTypeAction(controller.deleteEmailAddressType) + ..addOnUpdateListEmailAddressAction(controller.updateListEmailAddress) + ..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion) + ..addOnFocusNextAddressAction(controller.handleFocusNextAddressAction) + ).build(); + } else { + return const SizedBox.shrink(); + } + }), + ], + ); + } + + 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.colorDividerComposer, 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(), + Material( + type: MaterialType.circle, + color: Colors.transparent, + child: TextButton( + onPressed: controller.toggleDisplayAttachments, + child: Text( + expandModeAttachment == ExpandMode.EXPAND + ? AppLocalizations.of(context).hide + : '${AppLocalizations.of(context).showAll} (${uploadFilesState.length})', + style: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 12, + color: AppColor.colorTextButton + ) + ) + ) + ) + ], + ); + } + + Widget _buildAttachmentsList( + BuildContext context, + List uploadFilesState, + ExpandMode expandMode + ) { + const double maxHeightItem = 60; + if (expandMode == ExpandMode.EXPAND) { + if (PlatformInfo.isWeb) { + return const SizedBox.shrink(); + } 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) => AttachmentFileComposerBuilder( + uploadFilesState[index], + onDeleteAttachmentAction: (attachment) => controller.deleteAttachmentUploaded(attachment.uploadTaskId) + ) + ); + }); + } + } else { + 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) => AttachmentFileComposerBuilder( + 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 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) => AttachmentFileComposerBuilder( + 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.saveEmailAsDrafts(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.lineItemListColor + ), + 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.saveEmailAsDrafts(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 49022051c..693396abf 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -235,12 +235,10 @@ class ComposerController extends BaseController { } @override - void onReady() async { + void onReady() { _initEmail(); - _getAllIdentities(); if (!BuildUtils.isWeb) { - Future.delayed(const Duration(milliseconds: 500), () => - _checkContactPermission()); + Future.delayed(const Duration(milliseconds: 500), _checkContactPermission); } super.onReady(); } @@ -392,6 +390,7 @@ class ComposerController extends BaseController { _onChangeCursorOnMobile(coordinates, context); }, ); + _getAllIdentities(); } void _initEmail() { @@ -1397,7 +1396,6 @@ class ComposerController extends BaseController { if (newIdentity != null) { await _applyIdentityForAllFieldComposer(formerIdentity, newIdentity); } - return Future.value(null); } bool get _isMobileApp { @@ -1417,7 +1415,7 @@ class ComposerController extends BaseController { } if (!_isMobileApp) { - _removeSignature(); + await _removeSignature(); } } // Add new identity @@ -1426,10 +1424,8 @@ class ComposerController extends BaseController { } if (!_isMobileApp && newIdentity.signatureAsString.isNotEmpty == true) { - _applySignature(newIdentity.signatureAsString.asSignatureHtml()); + await _applySignature(newIdentity.signatureAsString.asSignatureHtml()); } - - return Future.value(null); } Future _applyBccEmailAddressFromIdentity(Set listEmailAddress) { @@ -1458,20 +1454,20 @@ class ComposerController extends BaseController { _updateStatusEmailSendButton(); } - void _applySignature(String signature) { + Future _applySignature(String signature) async { if (BuildUtils.isWeb) { richTextWebController.editorController.insertSignature(signature); } else { - htmlEditorApi?.insertSignature(signature); + await htmlEditorApi?.insertSignature(signature); } } - void _removeSignature() { + Future _removeSignature() async { log('ComposerController::_removeSignature():'); if (BuildUtils.isWeb) { richTextWebController.editorController.removeSignature(); } else { - htmlEditorApi?.removeSignature(); + await htmlEditorApi?.removeSignature(); } } @@ -1572,9 +1568,14 @@ class ComposerController extends BaseController { } } - void closeComposer() { - FocusManager.instance.primaryFocus?.unfocus(); - popBack(); + void closeComposer(BuildContext context) { + FocusScope.of(context).unfocus(); + + if (PlatformInfo.isWeb) { + mailboxDashBoardController.closeComposerOverlay(); + } else { + popBack(); + } } void _onEditorFocusOnMobile() { @@ -1660,6 +1661,7 @@ class ComposerController extends BaseController { richTextWebController.editorController.setFullScreen(); onChangeTextEditorWeb(initContent); richTextWebController.setEnableCodeView(); + _getAllIdentities(); } void handleOnFocusHtmlEditorWeb() { diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index 66a0a162e..8d217e234 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -1,49 +1,26 @@ -import 'package:core/presentation/extensions/capitalize_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/html_extension.dart'; -import 'package:core/presentation/extensions/string_extension.dart'; -import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/utils/html_transformer/html_utils.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/context_menu/simple_context_menu_action_builder.dart'; import 'package:core/presentation/views/image/avatar_builder.dart'; -import 'package:core/presentation/views/list/sliver_grid_delegate_fixed_height.dart'; import 'package:core/presentation/views/responsive/responsive_widget.dart'; -import 'package:core/presentation/views/text/text_field_builder.dart'; -import 'package:core/utils/app_logger.dart'; -import 'package:core/utils/direction_utils.dart'; -import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:file_picker/file_picker.dart'; -import 'package:filesize/filesize.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:jmap_dart_client/jmap/identities/identity.dart'; import 'package:model/email/email_action_type.dart'; -import 'package:model/email/prefix_email_address.dart'; -import 'package:model/mailbox/expand_mode.dart'; import 'package:rich_text_composer/rich_text_composer.dart'; import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart'; -import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.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/widgets/attachment_file_composer_builder.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.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/features/composer/presentation/base_composer_view.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/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart'; -class ComposerView extends GetWidget - with AppLoaderMixin, RichTextButtonMixin, ComposerLoadingMixin { - - final responsiveUtils = Get.find(); - final imagePaths = Get.find(); +class ComposerView extends BaseComposerView { ComposerView({Key? key}) : super(key: key); @@ -58,127 +35,114 @@ class ComposerView extends GetWidget Widget _buildComposerViewForMobile(BuildContext context) { return WillPopScope( - onWillPop: () async { - controller.saveEmailAsDrafts(context, canPop: false); - return true; - }, - child: LayoutBuilder( - builder: (context, constraints) => GestureDetector( - onTap: () { - controller.clearFocusEditor(context); - }, - child: Scaffold( - backgroundColor: Colors.white, - resizeToAvoidBottomInset: false, - body: SafeArea( - right: responsiveUtils.isLandscapeMobile(context), - left: responsiveUtils.isLandscapeMobile(context), + onWillPop: () async { + controller.saveEmailAsDrafts(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: AppColor.colorBackgroundKeyboard, + 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: [ - Obx(() => _buildAppBar(context, controller.isEnableEmailSendButton.value)), - const Divider(color: AppColor.colorDividerComposer, height: 1), - Expanded(child: _buildBodyMobile(context, constraints.maxWidth)) - ]) + color: Colors.white, + child: Column(children: [ + buildAppBar(context), + buildDivider(), + Expanded(child: _buildBodyMobile(context)) + ]) ), ), - ) - ), - ) + ); + }); + }) + ), + ) ); } Widget _buildComposerViewForTablet(BuildContext context) { return WillPopScope( - onWillPop: () async { - controller.saveEmailAsDrafts(context, canPop: false); - return true; - }, - child: GestureDetector( - onTap: () { - controller.clearFocusEditor(context); - }, - child: Scaffold( - backgroundColor: Colors.black38, - body: LayoutBuilder(builder: (context, constraints) { - return KeyboardRichText( - richTextController: controller.keyboardRichTextController, - keyBroadToolbar: RichTextKeyboardToolBar( - backgroundKeyboardToolBarColor: AppColor.colorBackgroundKeyboard, - 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, - ), - child: Align(alignment: Alignment.center, child: Card( - color: Colors.transparent, - elevation: 20, - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), - shadowColor: Colors.transparent, - child: Container( - decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(24))), - width: responsiveUtils.getSizeScreenWidth(context) * 0.9, - height: responsiveUtils.getSizeScreenHeight(context) * 0.9, - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(24)), - child: SafeArea( - child: Column(children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: _buildAppBar(context, controller.isEnableEmailSendButton.value)), - const Padding(padding: EdgeInsets.only(top: 8), child: Divider(color: AppColor.colorDividerComposer, height: 1)), - Expanded(child: _buildBodyTablet(context)), - const Divider(color: AppColor.colorDividerComposer, height: 1), - Obx(() => _buildBottomBar(context, controller.isEnableEmailSendButton.value)), - ]), - ) - ) + onWillPop: () async { + controller.saveEmailAsDrafts(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: AppColor.colorBackgroundKeyboard, + 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(), + buildBottomBar(context), + ]) ) - )), - ); - }) - ) + ) + ), + )), + ); + }); + }) ) + ) ); } - Widget _buildAppBar(BuildContext context, bool isEnableSendButton) { - return Container( - padding: responsiveUtils.isMobile(context) && responsiveUtils.isLandscapeMobile(context) - ? const EdgeInsets.all(8) - : EdgeInsets.zero, - color: Colors.white, - child: Row( - children: [ - buildIconWeb( - icon: SvgPicture.asset(imagePaths.icClose, width: 30, height: 30, fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).close, - iconPadding: EdgeInsets.zero, - onTap: () => controller.saveEmailAsDrafts(context)), - Expanded(child: _buildTitleComposer(context)), - if (responsiveUtils.isScreenWithShortestSide(context)) - buildIconWeb( - icon: SvgPicture.asset( - isEnableSendButton ? 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) { + @override + List popUpMoreActionMenu(BuildContext context) { return [ PopupMenuItem( padding: const EdgeInsets.symmetric(horizontal: 8), @@ -199,67 +163,6 @@ class ComposerView extends GetWidget ]; } - Widget _buildBottomBar(BuildContext context, bool isEnableSendButton) { - return Container( - padding: const EdgeInsets.symmetric(vertical: 16), - color: Colors.white, - child: Stack( - alignment: Alignment.centerRight, - children: [ - Row(mainAxisAlignment: MainAxisAlignment.center, - children: [ - buildTextButton( - AppLocalizations.of(context).cancel, - textStyle: const TextStyle(fontWeight: FontWeight.w500, fontSize: 17, color: AppColor.lineItemListColor), - backgroundColor: AppColor.emailAddressChipColor, - width: 150, - height: 44, - radius: 10, - onTap: () => controller.closeComposer()), - 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.saveEmailAsDrafts(context)), - const SizedBox(width: 12), - buildTextButton( - AppLocalizations.of(context).send, - width: 150, - height: 44, - radius: 10, - onTap: () => controller.sendEmailAction(context)), - ] - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - buildIconWithUpperMenu( - SvgPicture.asset(imagePaths.icRequestReadReceipt), - context, - _popUpMoreActionMenu(context), - controller.openPopupMenuAction) - ]), - ], - ), - ); - } - - Widget _buildTitleComposer(BuildContext context) { - return Obx(() => Text( - controller.subjectEmail.isNotEmpty == true - ? controller.subjectEmail.value ?? '' - : AppLocalizations.of(context).new_message.capitalizeFirstEach, - overflow: TextOverflow.ellipsis, - maxLines: 1, - textAlign: TextAlign.center, - style: TextStyle(fontSize: responsiveUtils.isMobile(context) ? 17 : 24, fontWeight: FontWeight.bold, color: Colors.black), - )); - } - List _pickAttachmentsActionTiles(BuildContext context) { return [ _pickPhotoAndVideoAction(context), @@ -286,255 +189,16 @@ class ComposerView extends GetWidget .build(); } - Widget _buildFromEmailAddress(BuildContext context) { - return Obx(() { - return Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 0 : responsiveUtils.isMobile(context) ? 16 : 0, - right: AppUtils.isDirectionRTL(context) ? responsiveUtils.isMobile(context) ? 16 : 0 : 0, - top: 12, - bottom: 12), - child: Row(children: [ - Text( - '${AppLocalizations.of(context).from_email_address_prefix}:', - style: const TextStyle( - fontSize: 15, - color: AppColor.colorHintEmailAddressInput - ) - ), - if (controller.listIdentities.isNotEmpty) - Padding( - padding: const EdgeInsets.only(left: 12), - child: DropdownButtonHideUnderline(child: DropdownButton2( - isExpanded: true, - customButton: SvgPicture.asset(imagePaths.icEditIdentity), - items: controller.listIdentities.map((item) => DropdownMenuItem( - value: item, - child: Container( - alignment: Alignment.centerLeft, - padding: const EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: item == controller.identitySelected.value - ? AppColor.colorBgMenuItemDropDownSelected - : Colors.transparent - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - item.name ?? '', - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.normal, - color: Colors.black - ), - maxLines: 1, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap, - ), - Text( - item.email ?? '', - style: const TextStyle( - fontSize: 13, - fontWeight: FontWeight.normal, - color: AppColor.colorHintSearchBar - ), - maxLines: 1, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap, - ) - ] - ), - ), - )).toList(), - onChanged: (newIdentity) => controller.selectIdentity(newIdentity), - dropdownStyleData: DropdownStyleData( - maxHeight: 240, - width: 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: Padding( - padding: EdgeInsets.only( - right: AppUtils.isDirectionRTL(context) ? 12 : 8, - left: AppUtils.isDirectionRTL(context) ? 8 : 12 - ), - child: Text( - controller.identitySelected.value != null - ? (controller.identitySelected.value?.email ?? '').withUnicodeCharacter - : (controller.userProfile?.email ?? '').withUnicodeCharacter, - maxLines: 1, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap, - style: const TextStyle( - fontSize: 17, - fontWeight: FontWeight.normal, - color: AppColor.colorEmailAddressPrefix - ) - ) - )), - ]), - ); - }); - } - - Widget _buildEmailAddress(BuildContext context) { - return Column( - children: [ - Obx(() => Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 0 : responsiveUtils.isMobile(context) ? 16 : 0, - right: AppUtils.isDirectionRTL(context) ? responsiveUtils.isMobile(context) ? 16 : 0 : 0, - ), - child: (EmailAddressInputBuilder(context, imagePaths, - PrefixEmailAddress.to, - controller.listToEmailAddress, - controller.listEmailAddressType, - expandMode: controller.toAddressExpandMode.value, - controller: controller.toEmailAddressController, - focusNode: controller.toAddressFocusNode, - autoDisposeFocusNode: false, - keyTagEditor: controller.keyToEmailTagEditor, - isInitial: controller.isInitialRecipient.value) - ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) - ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) - ..addOnAddEmailAddressTypeAction((prefixEmailAddress) => controller.addEmailAddressType(prefixEmailAddress)) - ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress)) - ..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word))) - .build() - )), - Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.cc) == true - ? const Divider(color: AppColor.colorDividerComposer, height: 1) - : const SizedBox.shrink()), - Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.cc) == true - ? Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 0 : responsiveUtils.isMobile(context) ? 16 : 0, - right: AppUtils.isDirectionRTL(context) ? responsiveUtils.isMobile(context) ? 16 : 0 : 0, - ), - child: (EmailAddressInputBuilder(context, imagePaths, - PrefixEmailAddress.cc, - controller.listCcEmailAddress, - controller.listEmailAddressType, - focusNode: controller.ccAddressFocusNode, - expandMode: controller.ccAddressExpandMode.value, - controller: controller.ccEmailAddressController, - keyTagEditor: controller.keyCcEmailTagEditor, - isInitial: controller.isInitialRecipient.value,) - ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) - ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) - ..addOnDeleteEmailAddressTypeAction((prefixEmailAddress) => controller.deleteEmailAddressType(prefixEmailAddress)) - ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress)) - ..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word))) - .build()) - : const SizedBox.shrink() - ), - Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.bcc) == true - ? const Divider(color: AppColor.colorDividerComposer, height: 1) - : const SizedBox.shrink()), - Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.bcc) == true - ? Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 0 : responsiveUtils.isMobile(context) ? 16 : 0, - right: AppUtils.isDirectionRTL(context) ? responsiveUtils.isMobile(context) ? 16 : 0 : 0, - ), - child: (EmailAddressInputBuilder(context, imagePaths, - PrefixEmailAddress.bcc, - controller.listBccEmailAddress, - controller.listEmailAddressType, - focusNode: controller.bccAddressFocusNode, - expandMode: controller.bccAddressExpandMode.value, - controller: controller.bccEmailAddressController, - keyTagEditor: controller.keyBccEmailTagEditor, - isInitial: controller.isInitialRecipient.value,) - ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) - ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) - ..addOnDeleteEmailAddressTypeAction((prefixEmailAddress) => controller.deleteEmailAddressType(prefixEmailAddress)) - ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress)) - ..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word))) - .build()) - : const SizedBox.shrink() - ), - ], - ); - } - - Widget _buildSubjectEmail(BuildContext context) { - log('ComposerView::_buildSubjectEmail(): '); - return Row( - children: [ - Padding( - padding: EdgeInsets.only( - right: AppUtils.isDirectionRTL(context) ? 0 : 8, - left: AppUtils.isDirectionRTL(context) ? 8 : 0 - ), - child: Text( - '${AppLocalizations.of(context).subject_email}:', - style: const TextStyle( - fontSize: 15, - color: AppColor.colorHintEmailAddressInput - ) - ) - ), - Expanded(child: TextFieldBuilder( - key: const Key('subject_email_input'), - cursorColor: AppColor.colorTextButton, - maxLines: responsiveUtils.isMobile(context) ? null : 1, - focusNode: controller.subjectEmailInputFocusNode, - textDirection: DirectionUtils.getDirectionByLanguage(context), - onTextChange: controller.setSubjectEmail, - textStyle: const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal), - decoration: const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none), - controller: controller.subjectEmailInputController, - )) - ] - ); - } - - Widget _buildBodyMobile(BuildContext context, double maxWidth) { - return KeyboardRichText( - keyBroadToolbar: RichTextKeyboardToolBar( - backgroundKeyboardToolBarColor: AppColor.colorBackgroundKeyboard, - isLandScapeMode: responsiveUtils.isLandscapeMobile(context), - insertAttachment: controller.isNetworkConnectionAvailable - ? () => controller.openPickAttachmentMenu(context, _pickAttachmentsActionTiles(context)) - : null, - insertImage: controller.isNetworkConnectionAvailable - ? () => controller.insertImage(context, 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, - ), - richTextController: controller.keyboardRichTextController, - child: SingleChildScrollView( - controller: controller.scrollController, - physics: const ClampingScrollPhysics(), - child: Column( - children: [ - _buildHeaderEditorMobile(context), - _buildComposerEditor(context), - SizedBox(height: controller.maxKeyBoardHeight), - ], - ), + Widget _buildBodyMobile(BuildContext context) { + return SingleChildScrollView( + controller: controller.scrollController, + physics: const ClampingScrollPhysics(), + child: Column( + children: [ + _buildHeaderEditorMobile(context), + _buildComposerEditor(context), + SizedBox(height: controller.maxKeyBoardHeight), + ], ), ); } @@ -543,17 +207,13 @@ class ComposerView extends GetWidget return Column( key: controller.headerEditorMobileWidgetKey, children: [ - _buildFromEmailAddress(context), - Obx(() => controller.identitySelected.value != null - ? const Divider(color: AppColor.colorDividerComposer, height: 1) - : const SizedBox.shrink()), - _buildEmailAddress(context), - const Divider(color: AppColor.colorDividerComposer, height: 1), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: _buildSubjectEmail(context)), - const Divider(color: AppColor.colorDividerComposer, height: 1), - _buildAttachmentsWidget(context), + buildFromEmailAddress(context), + buildDivider(), + buildEmailAddress(context), + buildDivider(), + buildSubjectEmail(context), + buildDivider(), + buildAttachmentsWidget(context), buildInlineLoadingView(controller), ], ); @@ -564,76 +224,40 @@ class ComposerView extends GetWidget controller: controller.scrollController, physics: const ClampingScrollPhysics(), child: Column(children: [ - Padding( - padding: EdgeInsets.only( - right: AppUtils.isDirectionRTL(context) ? 16 : 0, - left: AppUtils.isDirectionRTL(context) ? 0 : 16 + 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() ), - child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ - 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()), - Expanded(child: Padding( - padding: const EdgeInsets.only(left: 16), - child: Column(children: [ - _buildFromEmailAddress(context), - Obx(() => controller.identitySelected.value != null - ? const Divider(color: AppColor.colorDividerComposer, height: 1) - : const SizedBox.shrink()), - _buildEmailAddress(context), - const Divider(color: AppColor.colorDividerComposer, height: 1), - Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 16 : 0, - right: AppUtils.isDirectionRTL(context) ? 0 : 16, - ), - child: _buildSubjectEmail(context) - ), - ]), - )) - ])), - const Divider(color: AppColor.colorDividerComposer, height: 1), - Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 25 : 60, - right: AppUtils.isDirectionRTL(context) ? 60 : 25, - ), - child: Column(children: [ - _buildAttachmentsWidget(context), - buildInlineLoadingView(controller), - _buildComposerEditor(context), - ]) - ) + const SizedBox(width: 16), + Expanded(child: Column(children: [ + buildFromEmailAddress(context), + buildDivider(), + buildEmailAddress(context), + buildDivider(), + buildSubjectEmail(context), + ])) + ]), + buildDivider(), + buildAttachmentsWidget(context), + buildInlineLoadingView(controller), + _buildComposerEditor(context), ]) ); } - Widget _buildAttachmentsWidget(BuildContext context) { - return Obx(() { - final uploadAttachments = controller.uploadController.listUploadAttachments; - if (uploadAttachments.isEmpty) { - return const SizedBox.shrink(); - } else { - return Column(children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: _buildAttachmentsTitle(context, - uploadAttachments, - controller.expandModeAttachments.value)), - Padding( - padding: const EdgeInsets.only(bottom: 8, left: 16, right: 16), - child: _buildAttachmentsList(context, - uploadAttachments, - controller.expandModeAttachments.value)) - ]); - } - }); - } - Widget _buildComposerEditor(BuildContext context) { return Obx(() { final argsComposer = controller.composerArguments.value; @@ -645,7 +269,7 @@ class ComposerView extends GetWidget switch(argsComposer.emailActionType) { case EmailActionType.compose: case EmailActionType.composeFromEmailAddress: - return _buildHtmlEditor(context); + return _buildHtmlEditor(context, initialContent: HtmlExtension.editorStartTags); case EmailActionType.edit: return controller.emailContentsViewState.value.fold( (failure) => _buildHtmlEditor(context, initialContent: HtmlExtension.editorStartTags), @@ -687,7 +311,7 @@ class ComposerView extends GetWidget controller.removeFocusAllInputEditorHeader(); }, child: Padding( - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 20), + padding: ComposerStyle.getEditorPadding(context, responsiveUtils), child: HtmlEditor( key: const Key('composer_editor'), minHeight: 550, @@ -699,97 +323,4 @@ class ComposerView extends GetWidget ), ); } - - 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(), - Material( - type: MaterialType.circle, - color: Colors.transparent, - child: TextButton( - child: Text( - expandModeAttachment == ExpandMode.EXPAND - ? AppLocalizations.of(context).hide - : '${AppLocalizations.of(context).showAll} (${uploadFilesState.length})', - style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 12, color: AppColor.colorTextButton)), - onPressed: () => controller.toggleDisplayAttachments() - ) - ) - ], - ); - } - - Widget _buildAttachmentsList( - BuildContext context, - List uploadFilesState, - ExpandMode expandMode - ) { - const double maxHeightItem = 60; - if (expandMode == ExpandMode.EXPAND) { - 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: _getMaxItemRowListAttachment(context, constraints), - crossAxisSpacing: 8.0, - mainAxisSpacing: 8.0), - itemBuilder: (context, index) => AttachmentFileComposerBuilder( - uploadFilesState[index], - onDeleteAttachmentAction: (attachment) => - controller.deleteAttachmentUploaded(attachment.uploadTaskId)) - ); - }); - } else { - return LayoutBuilder(builder: (context, constraints) { - return Align( - alignment: Alignment.centerLeft, - 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) => AttachmentFileComposerBuilder( - uploadFilesState[index], - itemMargin: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 8 : 0, - right: AppUtils.isDirectionRTL(context) ? 0 : 8, - ), - maxWidth: _getMaxWidthItemListAttachment(context, constraints), - onDeleteAttachmentAction: (attachment) => - controller.deleteAttachmentUploaded(attachment.uploadTaskId)) - ) - ) - ); - }); - } - } - - int _getMaxItemRowListAttachment(BuildContext context, BoxConstraints constraints) { - if (constraints.maxWidth < ResponsiveUtils.minTabletWidth) { - return 2; - } else if (constraints.maxWidth < ResponsiveUtils.minTabletLargeWidth) { - return 3; - } else { - return 4; - } - } - - double _getMaxWidthItemListAttachment(BuildContext context, BoxConstraints constraints) { - return constraints.maxWidth / _getMaxItemRowListAttachment(context, constraints); - } } \ 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 9698a065b..d7b49ff32 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -1,50 +1,25 @@ -import 'package:core/presentation/extensions/capitalize_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/html_extension.dart'; -import 'package:core/presentation/extensions/string_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/image/avatar_builder.dart'; import 'package:core/presentation/views/responsive/responsive_widget.dart'; -import 'package:core/presentation/views/text/text_field_builder.dart'; -import 'package:core/utils/app_logger.dart'; import 'package:core/utils/direction_utils.dart'; -import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:file_picker/file_picker.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/email_action_type.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/base_composer_view.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.dart'; +import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/email_editor_widget.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/toolbar_rich_text_builder.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/features/email/domain/state/get_email_content_state.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart'; -class ComposerView extends GetWidget - with AppLoaderMixin, RichTextButtonMixin, ComposerLoadingMixin { - - final responsiveUtils = Get.find(); - final imagePaths = Get.find(); - final appToast = Get.find(); +class ComposerView extends BaseComposerView { ComposerView({Key? key}) : super(key: key); @@ -53,83 +28,59 @@ class ComposerView extends GetWidget return ResponsiveWidget( responsiveUtils: responsiveUtils, mobile: Scaffold( - backgroundColor: Colors.white, - body: Container( - color: Colors.white, - child: LayoutBuilder(builder: (context, constraints) => PointerInterceptor(child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Obx(() => _buildAppBarForMobile(context, controller.isEnableEmailSendButton.value)), - const Divider(color: AppColor.colorDividerComposer, height: 1), - _buildFromEmailAddress(context), - Obx(() => controller.identitySelected.value != null - ? const Divider(color: AppColor.colorDividerComposer, height: 1) - : const SizedBox.shrink()), - _buildEmailAddress(context, constraints), - const Divider(color: AppColor.colorDividerComposer, height: 1), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: _buildSubjectEmail(context)), - const Divider(color: AppColor.colorDividerComposer, height: 1), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: _buildListButton(context, constraints)), - const Divider(color: AppColor.colorDividerComposer, height: 1), - Expanded(child: Column( - children: [ - _buildAttachmentsWidget(context), - ToolbarRichTextWebBuilder(richTextWebController: controller.richTextWebController), - buildInlineLoadingView(controller), - Expanded(child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: _buildEditorForm(context) - )) - ] - )), - ] - ))) - ) + backgroundColor: Colors.white, + body: Container( + color: Colors.white, + child: LayoutBuilder(builder: (context, constraints) { + return PointerInterceptor(child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildAppBar(context), + buildDivider(), + buildFromEmailAddress(context), + buildDivider(), + _buildEmailAddressWeb(context, constraints), + buildDivider(), + buildSubjectEmail(context), + buildDivider(), + Padding( + padding: const EdgeInsetsDirectional.symmetric(horizontal: 16), + child: _buildListButton(context, constraints) + ), + buildDivider(), + buildAttachmentsWidget(context), + ToolbarRichTextWebBuilder( + richTextWebController: controller.richTextWebController, + padding: ComposerStyle.getRichTextButtonPadding(context, responsiveUtils), + ), + buildInlineLoadingView(controller), + Expanded(child: _buildEditorForm(context)) + ] + )); + }) + ) ), desktop: Obx(() { return Stack(children: [ if (controller.screenDisplayMode.value == ScreenDisplayMode.normal) - if (AppUtils.isDirectionRTL(context)) - Positioned(left: 5, bottom: 5, child: Card( - elevation: 20, - color: Colors.transparent, - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), - child: Container( - decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(24))), - width: responsiveUtils.getSizeScreenWidth(context) * 0.5, - height: responsiveUtils.getSizeScreenHeight(context) * 0.75, - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(24)), - child: LayoutBuilder(builder: (context, constraints) => - PointerInterceptor(child: _buildBodyForDesktop(context, constraints)) - ) + PositionedDirectional(end: 5, bottom: 5, child: Card( + elevation: 20, + color: Colors.transparent, + 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: responsiveUtils.getSizeScreenWidth(context) * 0.5, + height: responsiveUtils.getSizeScreenHeight(context) * 0.75, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(ComposerStyle.radius)), + child: LayoutBuilder(builder: (context, constraints) => + PointerInterceptor(child: _buildBodyForDesktop(context, constraints)) ) ) - )) - else - Positioned(right: 5, bottom: 5, child: Card( - elevation: 20, - color: Colors.transparent, - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), - child: Container( - decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(24))), - width: responsiveUtils.getSizeScreenWidth(context) * 0.5, - height: responsiveUtils.getSizeScreenHeight(context) * 0.75, - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(24)), - child: LayoutBuilder(builder: (context, constraints) => - PointerInterceptor(child: _buildBodyForDesktop(context, constraints)) - ) - ) - ) - )), + ) + )), if (controller.screenDisplayMode.value == ScreenDisplayMode.minimize) - if (AppUtils.isDirectionRTL(context)) - Positioned(left: 5, bottom: 5, child: Card( + PositionedDirectional(end: 5, bottom: 5, child: Card( elevation: 20, color: Colors.transparent, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), @@ -141,7 +92,7 @@ class ComposerView extends GetWidget borderRadius: const BorderRadius.all(Radius.circular(16)), child: PointerInterceptor(child: Row(children: [ Padding( - padding: const EdgeInsets.only(left: 10), + padding: const EdgeInsetsDirectional.only(start: 10), child: buildIconWeb( icon: SvgPicture.asset(imagePaths.icCloseMailbox, fit: BoxFit.fill), tooltip: AppLocalizations.of(context).saveAndClose, @@ -156,43 +107,8 @@ class ComposerView extends GetWidget tooltip: AppLocalizations.of(context).show, onTap: () => controller.displayScreenTypeComposerAction(ScreenDisplayMode.normal)), Expanded(child: Padding( - padding: const EdgeInsets.only(left: 16, right: 80), - child: _buildTitleComposer(context), - )), - ])) - ) - ) - )) - else - Positioned(right: 5, bottom: 5, child: Card( - elevation: 20, - color: Colors.transparent, - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), - child: Container( - decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(16))), - width: 500, - height: 50, - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(16)), - child: PointerInterceptor(child: Row(children: [ - Padding( - padding: const EdgeInsets.only(left: 10), - child: buildIconWeb( - icon: SvgPicture.asset(imagePaths.icCloseMailbox, fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).saveAndClose, - onTap: () => controller.saveEmailAsDrafts(context) - )), - buildIconWeb( - icon: SvgPicture.asset(imagePaths.icFullScreenComposer, fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).fullscreen, - onTap: () => controller.displayScreenTypeComposerAction(ScreenDisplayMode.fullScreen)), - buildIconWeb( - icon: SvgPicture.asset(imagePaths.icChevronUp, fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).show, - onTap: () => controller.displayScreenTypeComposerAction(ScreenDisplayMode.normal)), - Expanded(child: Padding( - padding: const EdgeInsets.only(left: 16, right: 80), - child: _buildTitleComposer(context), + padding: const EdgeInsetsDirectional.only(start: 16, end: 80), + child: buildTitleComposer(context), )), ])) ) @@ -203,13 +119,13 @@ class ComposerView extends GetWidget backgroundColor: Colors.black38, body: Align(alignment: Alignment.center, child: Card( color: Colors.transparent, - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(ComposerStyle.radius))), child: Container( - decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(24))), + decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(ComposerStyle.radius))), width: responsiveUtils.getSizeScreenWidth(context) * 0.85, height: responsiveUtils.getSizeScreenHeight(context) * 0.9, child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(24)), + borderRadius: const BorderRadius.all(Radius.circular(ComposerStyle.radius)), child: LayoutBuilder(builder: (context, constraints) => PointerInterceptor(child: _buildBodyForDesktop(context, constraints))) ) @@ -220,88 +136,81 @@ class ComposerView extends GetWidget ]); }), tablet: Scaffold( - backgroundColor: Colors.black38, - body: Align(alignment: Alignment.center, child: Card( - color: Colors.transparent, - shadowColor: Colors.transparent, - child: Container( - decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(24))), - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(24)), - child: LayoutBuilder(builder: (context, constraints) => - PointerInterceptor(child: _buildBodyForDesktop(context, constraints))) + backgroundColor: Colors.black38, + body: Center( + child: Card( + elevation: 20, + margin: ComposerStyle.getMarginForTabletWeb(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.getWidthForTabletWeb(context, responsiveUtils), + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(ComposerStyle.radius)), + child: LayoutBuilder(builder: (context, constraints) => PointerInterceptor( + child: _buildBodyForDesktop( + context, + constraints ) + )) ) - )) + ) + ) + ) ) ); } - Widget _buildAppBar(BuildContext context) { - return Row( - children: [ - buildIconWeb( + Widget _buildAppBarDesktop(BuildContext context) { + return Container( + height: 52, + padding: const EdgeInsetsDirectional.symmetric(horizontal: 32), + child: Row( + children: [ + buildIconWeb( minSize: 40, iconPadding: EdgeInsets.zero, icon: SvgPicture.asset(imagePaths.icCloseMailbox, fit: BoxFit.fill), tooltip: AppLocalizations.of(context).saveAndClose, - onTap: () => controller.saveEmailAsDrafts(context)), - if (responsiveUtils.isDesktop(context)) - Obx(() => buildIconWeb( - minSize: 40, - iconPadding: EdgeInsets.zero, - icon: SvgPicture.asset( - controller.screenDisplayMode.value == ScreenDisplayMode.fullScreen - ? imagePaths.icFullScreenExit - : imagePaths.icFullScreenComposer, - fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).fullscreen, - onTap: () => controller.displayScreenTypeComposerAction(controller.screenDisplayMode.value == ScreenDisplayMode.fullScreen - ? ScreenDisplayMode.normal - : ScreenDisplayMode.fullScreen))), - if (responsiveUtils.isDesktop(context)) - buildIconWeb( - minSize: 40, - iconPadding: EdgeInsets.zero, - icon: SvgPicture.asset(imagePaths.icMinimize, fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).minimize, - onTap: () => controller.displayScreenTypeComposerAction(ScreenDisplayMode.minimize)), - Expanded(child: _buildTitleComposer(context)), - const SizedBox(width: 100), - ] - ); - } - - Widget _buildAppBarForMobile(BuildContext context, bool isEnableSendButton) { - return Container( - padding: const EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10), - color: Colors.white, - child: Row( - children: [ - buildIconWeb( - icon: SvgPicture.asset(imagePaths.icClose, width: 30, height: 30, fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).saveAndClose, + onTap: () => controller.saveEmailAsDrafts(context) + ), + if (responsiveUtils.isDesktop(context)) + ...[ + Obx(() => buildIconWeb( + minSize: 40, iconPadding: EdgeInsets.zero, - onTap: () => controller.saveEmailAsDrafts(context)), - Expanded(child: _buildTitleComposer(context)), - buildIconWeb( icon: SvgPicture.asset( - isEnableSendButton ? imagePaths.icSendMobile : imagePaths.icSendDisable, - fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).send, - onTap: () => controller.sendEmailAction(context)), - buildIconWithLowerMenu( - SvgPicture.asset(imagePaths.icRequestReadReceipt), - context, - _popUpMoreActionMenu(context), - controller.openPopupMenuAction), - ] + controller.screenDisplayMode.value == ScreenDisplayMode.fullScreen + ? imagePaths.icFullScreenExit + : imagePaths.icFullScreenComposer, + fit: BoxFit.fill + ), + tooltip: AppLocalizations.of(context).fullscreen, + onTap: () => controller.displayScreenTypeComposerAction( + controller.screenDisplayMode.value == ScreenDisplayMode.fullScreen + ? ScreenDisplayMode.normal + : ScreenDisplayMode.fullScreen + ) + )), + buildIconWeb( + minSize: 40, + iconPadding: EdgeInsets.zero, + icon: SvgPicture.asset(imagePaths.icMinimize, fit: BoxFit.fill), + tooltip: AppLocalizations.of(context).minimize, + onTap: () => controller.displayScreenTypeComposerAction(ScreenDisplayMode.minimize) + ), + ], + Expanded(child: buildTitleComposer(context)), + const SizedBox(width: 120), + ] ), ); } - List _popUpMoreActionMenu(BuildContext context) { + @override + List popUpMoreActionMenu(BuildContext context) { return [ PopupMenuItem( padding: const EdgeInsets.symmetric(horizontal: 8), @@ -335,7 +244,7 @@ class ComposerView extends GetWidget ]; } - Widget _buildBottomBar(BuildContext context, bool isEnableSendButton, BoxConstraints constraints) { + Widget _buildBottomBarDesktop(BuildContext context, BoxConstraints constraints) { return Container( padding: const EdgeInsets.symmetric(vertical: 16), color: Colors.white, @@ -343,51 +252,7 @@ class ComposerView extends GetWidget scrollDirection: Axis.horizontal, child: ConstrainedBox( constraints: constraints.widthConstraints(), - child: Stack( - alignment: Alignment.centerRight, - children: [ - Row(mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(width: 24), - buildButtonWrapText( - AppLocalizations.of(context).cancel, - textStyle: const TextStyle(fontWeight: FontWeight.w500, fontSize: 17, color: AppColor.lineItemListColor), - bgColor: AppColor.emailAddressChipColor, - minWidth: 150, - height: 44, - radius: 10, - onTap: () => controller.closeComposerWeb()), - const SizedBox(width: 12), - buildButtonWrapText( - AppLocalizations.of(context).save_to_drafts, - textStyle: const TextStyle(fontWeight: FontWeight.w500, fontSize: 17, color: AppColor.colorTextButton), - bgColor: AppColor.emailAddressChipColor, - minWidth: 150, - height: 44, - radius: 10, - onTap: () => controller.saveEmailAsDrafts(context)), - const SizedBox(width: 12), - buildButtonWrapText( - AppLocalizations.of(context).send, - minWidth: 150, - height: 44, - radius: 10, - onTap: () => controller.sendEmailAction(context)), - const SizedBox(width: 24), - ] - ), - if(!responsiveUtils.isMobile(context)) - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - buildIconWithUpperMenu( - SvgPicture.asset(imagePaths.icRequestReadReceipt), - context, - _popUpMoreActionMenu(context), - controller.openPopupMenuAction) - ]), - ], - ), + child: buildBottomBar(context), ), ), ); @@ -395,371 +260,115 @@ class ComposerView extends GetWidget Widget _buildBodyForDesktop(BuildContext context, BoxConstraints constraints) { return Column(children: [ - Padding(padding: const EdgeInsets.only(left: 20, right: 20, top: 8), child: _buildAppBar(context)), - const Padding(padding: EdgeInsets.only(top: 8), child: Divider(color: AppColor.colorDividerComposer, height: 1)), - Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 0 : 16, - right: AppUtils.isDirectionRTL(context) ? 16 : 0, + _buildAppBarDesktop(context), + buildDivider(), + 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() ), - child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ - 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()), - Expanded(child: Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 0 : 12, - right: AppUtils.isDirectionRTL(context) ? 12 : 0, - ), - child: Column(children: [ - _buildFromEmailAddress(context), - Obx(() => controller.identitySelected.value != null - ? const Divider(color: AppColor.colorDividerComposer, height: 1) - : const SizedBox.shrink()), - _buildEmailAddress(context, constraints), - const Divider(color: AppColor.colorDividerComposer, height: 1), - Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 16 : 0, - right: AppUtils.isDirectionRTL(context) ? 0 : 16, - ), - child: _buildSubjectEmail(context) - ), - const Divider(color: AppColor.colorDividerComposer, height: 1), - _buildListButton(context, constraints), - ]), - )) - ])), - const Divider(color: AppColor.colorDividerComposer, height: 1), - Expanded(child: Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? responsiveUtils.isMobile(context) ? 16 : 25 : responsiveUtils.isMobile(context) ? 16 : 60, - right: AppUtils.isDirectionRTL(context) ? responsiveUtils.isMobile(context) ? 16 : 60 : responsiveUtils.isMobile(context) ? 16 : 25, - ), - child: Column( - children: [ - _buildAttachmentsWidget(context), - ToolbarRichTextWebBuilder(richTextWebController: controller.richTextWebController), - buildInlineLoadingView(controller), - Expanded(child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: _buildEditorForm(context) - )) - ] - ))), - const Divider(color: AppColor.colorDividerComposer, height: 1), - Obx(() => _buildBottomBar(context, controller.isEnableEmailSendButton.value, constraints)), + const SizedBox(width: 16), + Expanded(child: Column(children: [ + buildFromEmailAddress(context), + buildDivider(), + _buildEmailAddressWeb(context, constraints), + buildDivider(), + buildSubjectEmail(context), + buildDivider(), + _buildListButton(context, constraints), + ])) + ] + ), + buildDivider(), + buildAttachmentsWidget(context), + ToolbarRichTextWebBuilder( + richTextWebController: controller.richTextWebController, + padding: ComposerStyle.getRichTextButtonPadding(context, responsiveUtils), + ), + buildInlineLoadingView(controller), + Expanded(child: _buildEditorForm(context)), + buildDivider(), + _buildBottomBarDesktop(context, constraints), ]); } - 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, - style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black), - )); - } - - Widget _buildFromEmailAddress(BuildContext context) { - return Obx(() { - return Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 0 : responsiveUtils.isMobile(context) ? 16 : 0, - right: AppUtils.isDirectionRTL(context) ? responsiveUtils.isMobile(context) ? 16 : 0 : 0, - top: 12, - bottom: 12), - child: Row(children: [ - Text( - '${AppLocalizations.of(context).from_email_address_prefix}:', - style: const TextStyle( - fontSize: 15, - color: AppColor.colorHintEmailAddressInput - ) - ), - if (controller.listIdentities.isNotEmpty) - Padding( - padding: const EdgeInsets.only(left: 12), - child: DropdownButtonHideUnderline( - child: DropdownButton2( - isExpanded: true, - customButton: SvgPicture.asset(imagePaths.icEditIdentity), - items: controller.listIdentities.map((item) => DropdownMenuItem( - value: item, - child: PointerInterceptor( - child: Container( - alignment: Alignment.centerLeft, - padding: const EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: item == controller.identitySelected.value - ? AppColor.colorBgMenuItemDropDownSelected - : Colors.transparent - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - item.name ?? '', - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.normal, - color: Colors.black), - maxLines: 1, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap - ), - Text( - item.email ?? '', - style: const TextStyle( - fontSize: 13, - fontWeight: FontWeight.normal, - color: AppColor.colorHintSearchBar), - maxLines: 1, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap - ) - ] - ), - ), - ), - )).toList(), - onChanged: (newIdentity) => controller.selectIdentity(newIdentity), - dropdownStyleData: DropdownStyleData( - maxHeight: 240, - width: 370, - 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: Padding( - padding: EdgeInsets.only( - right: AppUtils.isDirectionRTL(context) ? 12 : 8, - left: AppUtils.isDirectionRTL(context) ? 8 : 12, - ), - child: Text( - controller.identitySelected.value != null - ? (controller.identitySelected.value?.email ?? '').withUnicodeCharacter - : (controller.userProfile?.email ?? '').withUnicodeCharacter, - maxLines: 1, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap, - style: const TextStyle( - fontSize: 17, - fontWeight: FontWeight.normal, - color: AppColor.colorEmailAddressPrefix - ), - ) - )), - ]), - ); - }); - } - - Widget _buildEmailAddress(BuildContext context, BoxConstraints constraints) { - log('ComposerView::_buildEmailAddress(): height: ${constraints.maxHeight}'); + Widget _buildEmailAddressWeb(BuildContext context, BoxConstraints constraints) { return ConstrainedBox( - constraints: BoxConstraints(maxHeight: _getMaxHeightEmailAddressWidget(context, constraints)), - child: SingleChildScrollView( - controller: controller.scrollControllerEmailAddress, - child: Column( - children: [ - Obx(() => Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 0 : responsiveUtils.isMobile(context) ? 16 : 0, - right: AppUtils.isDirectionRTL(context) ? responsiveUtils.isMobile(context) ? 16 : 0 : 0 - ), - child: (EmailAddressInputBuilder(context, imagePaths, - PrefixEmailAddress.to, - controller.listToEmailAddress, - controller.listEmailAddressType, - expandMode: controller.toAddressExpandMode.value, - controller: controller.toEmailAddressController, - focusNode: controller.toAddressFocusNode, - autoDisposeFocusNode: false, - isInitial: controller.isInitialRecipient.value, - keyTagEditor: controller.keyToEmailTagEditor, - nextFocusNode: controller.getNextFocusOfToEmailAddress() - ) - ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) - ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) - ..addOnAddEmailAddressTypeAction((prefixEmailAddress) => controller.addEmailAddressType(prefixEmailAddress)) - ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress)) - ..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion) - ..addOnFocusNextAddressAction(controller.handleFocusNextAddressAction)) - .build() - )), - Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.cc) == true - ? const Divider(color: AppColor.colorDividerComposer, height: 1) - : const SizedBox.shrink()), - Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.cc) == true - ? Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 0 : responsiveUtils.isMobile(context) ? 16 : 0, - right: AppUtils.isDirectionRTL(context) ? responsiveUtils.isMobile(context) ? 16 : 0 : 0 - ), - child: (EmailAddressInputBuilder(context, imagePaths, - PrefixEmailAddress.cc, - controller.listCcEmailAddress, - controller.listEmailAddressType, - expandMode: controller.ccAddressExpandMode.value, - controller: controller.ccEmailAddressController, - isInitial: controller.isInitialRecipient.value, - keyTagEditor: controller.keyCcEmailTagEditor, - focusNode: controller.ccAddressFocusNode, - nextFocusNode: controller.getNextFocusOfCcEmailAddress() - ) - ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) - ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) - ..addOnDeleteEmailAddressTypeAction((prefixEmailAddress) => controller.deleteEmailAddressType(prefixEmailAddress)) - ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress)) - ..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion) - ..addOnFocusNextAddressAction(controller.handleFocusNextAddressAction)) - .build()) - : const SizedBox.shrink() - ), - Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.bcc) == true - ? const Divider(color: AppColor.colorDividerComposer, height: 1) - : const SizedBox.shrink()), - Obx(() => controller.listEmailAddressType.contains(PrefixEmailAddress.bcc) == true - ? Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 0 : responsiveUtils.isMobile(context) ? 16 : 0, - right: AppUtils.isDirectionRTL(context) ? responsiveUtils.isMobile(context) ? 16 : 0 : 0 - ), - child: (EmailAddressInputBuilder(context, imagePaths, - PrefixEmailAddress.bcc, - controller.listBccEmailAddress, - controller.listEmailAddressType, - expandMode: controller.bccAddressExpandMode.value, - controller: controller.bccEmailAddressController, - isInitial: controller.isInitialRecipient.value, - keyTagEditor: controller.keyBccEmailTagEditor, - focusNode: controller.bccAddressFocusNode, - nextFocusNode: controller.subjectEmailInputFocusNode - ) - ..addOnFocusEmailAddressChangeAction((prefixEmailAddress, focus) => controller.onEmailAddressFocusChange(prefixEmailAddress, focus)) - ..addOnShowFullListEmailAddressAction((prefixEmailAddress) => controller.showFullEmailAddress(prefixEmailAddress)) - ..addOnDeleteEmailAddressTypeAction((prefixEmailAddress) => controller.deleteEmailAddressType(prefixEmailAddress)) - ..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress)) - ..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion) - ..addOnFocusNextAddressAction(controller.handleFocusNextAddressAction)) - .build()) - : const SizedBox.shrink() - ), - ], - ), - ) - ); - } - - Widget _buildSubjectEmail(BuildContext context) { - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only( - right: AppUtils.isDirectionRTL(context) ? 0 : 8, - left: AppUtils.isDirectionRTL(context) ? 8 : 0, - top: 16 - ), - child: Text( - '${AppLocalizations.of(context).subject_email}:', - style: const TextStyle( - fontSize: 15, - color: AppColor.colorHintEmailAddressInput - ) - ) - ), - Expanded(child: TextFieldBuilder( - key: const Key('subject_email_input'), - cursorColor: AppColor.colorTextButton, - focusNode: controller.subjectEmailInputFocusNode, - onTextChange: controller.setSubjectEmail, - 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, - )) - ] + constraints: BoxConstraints(maxHeight: ComposerStyle.getMaxHeightEmailAddressWidget(context, constraints, responsiveUtils)), + child: SingleChildScrollView( + controller: controller.scrollControllerEmailAddress, + child: buildEmailAddress(context), + ) ); } Widget _buildListButton(BuildContext context, BoxConstraints constraints) { return Transform( - transform: Matrix4.translationValues( - AppUtils.isDirectionRTL(context) ? 0.0 : -5.0, - 0.0, - 0.0 - ), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 5), - child: Row(children: [ - buildIconWeb( - minSize: 40, - iconPadding: EdgeInsets.zero, - icon: SvgPicture.asset(imagePaths.icAttachmentsComposer, - width: 24, - height: 24, - colorFilter: const ColorFilter.mode(AppColor.colorTextButton, BlendMode.srcIn), - fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).attach_file, - onTap: () => controller.openFilePickerByType(context, FileType.any)), - const SizedBox(width: 4), - Obx(() { - final opacity = controller.richTextWebController.codeViewEnabled ? 0.5 : 1.0; - return AbsorbPointer( - absorbing: controller.richTextWebController.codeViewEnabled, - child: buildIconWeb( - minSize: 40, - iconPadding: EdgeInsets.zero, - icon: SvgPicture.asset( - imagePaths.icInsertImage, - colorFilter: ColorFilter.mode( - AppColor.colorTextButton.withOpacity(opacity), - BlendMode.srcIn), - fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).insertImage, - onTap: () => controller.insertImage(context, constraints.maxWidth)), - ); - }), - const SizedBox(width: 4), - Obx(() { - return buildIconWeb( - minSize: 40, - colorSelected: controller.richTextWebController.codeViewEnabled - ? AppColor.colorSelectedRichTextButton - : Colors.transparent, - iconPadding: EdgeInsets.zero, - icon: SvgPicture.asset( - imagePaths.icStyleCodeView, - colorFilter: const ColorFilter.mode(AppColor.colorTextButton, BlendMode.srcIn), - fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).codeView, - onTap: () => controller.richTextWebController.toggleCodeView()); - }), - ]) - ) + transform: Matrix4.translationValues( + DirectionUtils.isDirectionRTLByLanguage(context) ? 0.0 : -5.0, + 0.0, + 0.0 + ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5), + child: Row(children: [ + buildIconWeb( + minSize: 40, + iconPadding: EdgeInsets.zero, + icon: SvgPicture.asset(imagePaths.icAttachmentsComposer, + width: 24, + height: 24, + colorFilter: AppColor.colorTextButton.asFilter(), + fit: BoxFit.fill), + tooltip: AppLocalizations.of(context).attach_file, + onTap: () => controller.openFilePickerByType(context, FileType.any) + ), + const SizedBox(width: 4), + Obx(() { + final opacity = controller.richTextWebController.codeViewEnabled ? 0.5 : 1.0; + return AbsorbPointer( + absorbing: controller.richTextWebController.codeViewEnabled, + child: buildIconWeb( + minSize: 40, + iconPadding: EdgeInsets.zero, + icon: SvgPicture.asset( + imagePaths.icInsertImage, + colorFilter: AppColor.colorTextButton.withOpacity(opacity).asFilter(), + fit: BoxFit.fill), + tooltip: AppLocalizations.of(context).insertImage, + onTap: () => controller.insertImage(context, constraints.maxWidth) + ), + ); + }), + const SizedBox(width: 4), + Obx(() { + return buildIconWeb( + minSize: 40, + colorSelected: controller.richTextWebController.codeViewEnabled + ? AppColor.colorSelectedRichTextButton + : Colors.transparent, + iconPadding: EdgeInsets.zero, + icon: SvgPicture.asset( + imagePaths.icStyleCodeView, + colorFilter: AppColor.colorTextButton.asFilter(), + fit: BoxFit.fill), + tooltip: AppLocalizations.of(context).codeView, + onTap: controller.richTextWebController.toggleCodeView); + }), + ]) + ) ); } @@ -821,125 +430,13 @@ class ComposerView extends GetWidget } Widget _buildHtmlEditor(BuildContext context, String initContent) { - return EmailEditorWidget( - controller: controller, - content: initContent, - direction: AppUtils.getCurrentDirection(context), + return Padding( + padding: ComposerStyle.getEditorPadding(context, responsiveUtils), + child: EmailEditorWidget( + controller: controller, + content: initContent, + direction: AppUtils.getCurrentDirection(context), + ), ); } - - Widget _buildAttachmentsWidget(BuildContext context) { - return Obx(() { - final attachments = controller.uploadController.listUploadAttachments; - if (attachments.isNotEmpty) { - return Column(children: [ - Padding( - padding: EdgeInsets.only( - top: 4, - bottom: 4, - left: AppUtils.isDirectionRTL(context) ? responsiveUtils.isMobile(context) ? 16: 0 : responsiveUtils.isMobile(context) ? 16 : 20, - right: AppUtils.isDirectionRTL(context) ? responsiveUtils.isMobile(context) ? 16 : 20 : responsiveUtils.isMobile(context) ? 16: 0), - child: _buildAttachmentsTitle( - context, - attachments, - controller.expandModeAttachments.value)), - Padding( - padding: EdgeInsets.only( - left: responsiveUtils.isMobile(context) ? 16 : 10, - right: responsiveUtils.isMobile(context) ? 16 : 10), - child: _buildAttachmentsList( - context, - attachments, - controller.expandModeAttachments.value)) - ]); - } else { - return const SizedBox.shrink(); - } - }); - } - - 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(), - TextButton( - onPressed: () => controller.toggleDisplayAttachments(), - child: Material( - type: MaterialType.circle, - color: Colors.transparent, - child: Text( - expandModeAttachment == ExpandMode.EXPAND - ? AppLocalizations.of(context).hide - : '${AppLocalizations.of(context).showAll} (${uploadFilesState.length})', - style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 12, color: AppColor.colorTextButton)) - ), - ) - ], - ); - } - - Widget _buildAttachmentsList( - BuildContext context, - List uploadFilesState, - ExpandMode expandMode) { - if (expandMode == ExpandMode.COLLAPSE) { - return const SizedBox.shrink(); - } else { - return LayoutBuilder(builder: (context, constraints) { - return Align( - alignment: Alignment.centerLeft, - child: SizedBox( - height: 60, - 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) => AttachmentFileComposerBuilder( - uploadFilesState[index], - itemMargin: EdgeInsets.only( - right: AppUtils.isDirectionRTL(context) ? 0 : 8, - left: AppUtils.isDirectionRTL(context) ? 8 : 0 - ), - maxWidth: _getMaxWidthItemListAttachment(context, constraints), - onDeleteAttachmentAction: (attachment) => controller.deleteAttachmentUploaded(attachment.uploadTaskId)) - ), - ) - ) - ); - }); - } - } - - int _getMaxItemRowListAttachment(BuildContext context, BoxConstraints constraints) { - if (constraints.maxWidth < ResponsiveUtils.minTabletWidth) { - return 2; - } else if (constraints.maxWidth < ResponsiveUtils.minTabletLargeWidth) { - return 3; - } else { - return 4; - } - } - - double _getMaxWidthItemListAttachment(BuildContext context, BoxConstraints constraints) { - final currentWidth = constraints.maxWidth - 40; - return currentWidth / _getMaxItemRowListAttachment(context, constraints); - } - - double _getMaxHeightEmailAddressWidget(BuildContext context, BoxConstraints constraints) { - if (responsiveUtils.isDesktop(context)) { - return constraints.maxHeight > 0 ? constraints.maxHeight * 0.3 : 150.0; - } else { - return constraints.maxHeight > 0 ? constraints.maxHeight * 0.4 : 150.0; - } - } } \ No newline at end of file diff --git a/lib/features/composer/presentation/styles/composer_style.dart b/lib/features/composer/presentation/styles/composer_style.dart new file mode 100644 index 000000000..403405d1d --- /dev/null +++ b/lib/features/composer/presentation/styles/composer_style.dart @@ -0,0 +1,186 @@ + +import 'package:core/presentation/utils/responsive_utils.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:flutter/cupertino.dart'; + +class ComposerStyle { + + static const double radius = 24; + + static EdgeInsetsGeometry getAppBarPadding(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) { + return const EdgeInsetsDirectional.only(end: 8); + } else { + return const EdgeInsetsDirectional.only(start: 24, end: 32); + } + } + + static EdgeInsetsGeometry getFromAddressPadding(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) { + return const EdgeInsetsDirectional.symmetric(horizontal: 16, vertical: 12); + } else { + return const EdgeInsetsDirectional.symmetric(horizontal: 8, vertical: 12); + } + } + + static EdgeInsetsGeometry getToAddressPadding(BuildContext context, ResponsiveUtils responsiveUtils) { + if (PlatformInfo.isWeb) { + if (responsiveUtils.isMobile(context)) { + return const EdgeInsetsDirectional.only(start: 16, end: 8); + } else { + return const EdgeInsetsDirectional.symmetric(horizontal: 8); + } + } else { + if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) { + return const EdgeInsetsDirectional.symmetric(horizontal: 16); + } else { + return const EdgeInsetsDirectional.only(start: 8, end: 16); + } + } + } + + static EdgeInsetsGeometry getCcBccAddressPadding(BuildContext context, ResponsiveUtils responsiveUtils) { + if (PlatformInfo.isWeb) { + if (responsiveUtils.isMobile(context)) { + return const EdgeInsetsDirectional.only(start: 16); + } else { + return const EdgeInsetsDirectional.symmetric(horizontal: 8); + } + } else { + if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) { + return const EdgeInsetsDirectional.only(start: 16); + } else { + return const EdgeInsetsDirectional.symmetric(horizontal: 8); + } + } + } + + static EdgeInsetsGeometry getSubjectPadding(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) { + return const EdgeInsetsDirectional.symmetric(horizontal: 16, vertical: 8); + } else { + return const EdgeInsetsDirectional.only(start: 8, top: 8, bottom: 8, end: 16); + } + } + + static EdgeInsetsGeometry getSubjectWebPadding(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isMobile(context)) { + return const EdgeInsetsDirectional.symmetric(horizontal: 16); + } else { + return const EdgeInsetsDirectional.only(start: 8, end: 16); + } + } + + static EdgeInsetsGeometry getPadding(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) { + return const EdgeInsetsDirectional.symmetric(horizontal: 8); + } else { + return const EdgeInsetsDirectional.symmetric(horizontal: 32); + } + } + + static double getAppBarHeight(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) { + return 57; + } else { + return 65; + } + } + + static double getSpace(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) { + return 8; + } else { + return 12; + } + } + + static EdgeInsetsGeometry getAttachmentPadding(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) { + return const EdgeInsetsDirectional.symmetric(horizontal: 16); + } else { + return const EdgeInsetsDirectional.only(start: 88, end: 48); + } + } + + static EdgeInsetsGeometry getRichTextButtonPadding(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isMobile(context)) { + return const EdgeInsetsDirectional.symmetric(horizontal: 16, vertical: 8); + } else { + return const EdgeInsetsDirectional.only(start: 88, end: 48, top: 8, bottom: 8); + } + } + + static EdgeInsetsGeometry getEditorPadding(BuildContext context, ResponsiveUtils responsiveUtils) { + if (PlatformInfo.isWeb) { + if (responsiveUtils.isMobile(context)) { + return const EdgeInsetsDirectional.symmetric(horizontal: 16); + } else { + return const EdgeInsetsDirectional.only(start: 88, end: 48); + } + } else { + if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) { + return const EdgeInsetsDirectional.symmetric(horizontal: 16); + } else { + return const EdgeInsetsDirectional.only(start: 88, end: 48); + } + } + } + + static EdgeInsetsGeometry getMarginForTablet(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isPortraitTablet(context)) { + return const EdgeInsetsDirectional.all(24); + } else { + return const EdgeInsetsDirectional.symmetric(vertical: 24); + } + } + + static double getWidthForTablet(BuildContext context, ResponsiveUtils responsiveUtils) { + final currentWidth = responsiveUtils.getSizeScreenWidth(context); + if (responsiveUtils.isPortraitTablet(context)) { + return currentWidth; + } else { + return currentWidth * 0.7; + } + } + + static EdgeInsetsGeometry getMarginForTabletWeb(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isTablet(context)) { + return const EdgeInsetsDirectional.all(24); + } else { + return const EdgeInsetsDirectional.symmetric(vertical: 24); + } + } + + static double getWidthForTabletWeb(BuildContext context, ResponsiveUtils responsiveUtils) { + final currentWidth = responsiveUtils.getSizeScreenWidth(context); + if (responsiveUtils.isTablet(context)) { + return currentWidth; + } else { + return currentWidth * 0.7; + } + } + + static int getMaxItemRowListAttachment(BuildContext context, BoxConstraints constraints) { + if (constraints.maxWidth < ResponsiveUtils.minTabletWidth) { + return 2; + } else if (constraints.maxWidth < ResponsiveUtils.minTabletLargeWidth) { + return 4; + } else { + return 5; + } + } + + static double getMaxWidthItemListAttachment(BuildContext context, BoxConstraints constraints) { + return constraints.maxWidth / getMaxItemRowListAttachment(context, constraints); + } + + + static double getMaxHeightEmailAddressWidget(BuildContext context, BoxConstraints constraints, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isDesktop(context)) { + return constraints.maxHeight > 0 ? constraints.maxHeight * 0.3 : 150.0; + } else { + return constraints.maxHeight > 0 ? constraints.maxHeight * 0.4 : 150.0; + } + } +} \ No newline at end of file diff --git a/lib/features/composer/presentation/widgets/attachment_file_composer_builder.dart b/lib/features/composer/presentation/widgets/attachment_file_composer_builder.dart index 99ac4ca04..ed74e2dac 100644 --- a/lib/features/composer/presentation/widgets/attachment_file_composer_builder.dart +++ b/lib/features/composer/presentation/widgets/attachment_file_composer_builder.dart @@ -1,5 +1,10 @@ -import 'package:core/core.dart'; +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/style_utils.dart'; +import 'package:core/presentation/views/button/icon_button_web.dart'; +import 'package:core/utils/direction_utils.dart'; +import 'package:core/utils/platform_info.dart'; import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -8,7 +13,6 @@ import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart'; import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_status.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -import 'package:tmail_ui_user/main/utils/app_utils.dart'; typedef OnDeleteAttachmentAction = void Function(UploadFileState fileState); @@ -18,7 +22,7 @@ class AttachmentFileComposerBuilder extends StatelessWidget with AppLoaderMixin final UploadFileState fileState; final double? maxWidth; - final EdgeInsets? itemMargin; + final EdgeInsetsGeometry? itemMargin; final OnDeleteAttachmentAction? onDeleteAttachmentAction; final Widget? buttonAction; @@ -52,10 +56,9 @@ class AttachmentFileComposerBuilder extends StatelessWidget with AppLoaderMixin hoverColor: AppColor.primaryColor, onTap: () {}, leading: Padding( - padding: EdgeInsets.only( - left: AppUtils.isDirectionRTL(context) ? 0 : 8, - right: AppUtils.isDirectionRTL(context) ? 8 : 0, - bottom: BuildUtils.isWeb ? 6 : 14 + padding: const EdgeInsetsDirectional.only( + start: 8, + bottom: PlatformInfo.isWeb ? 6 : 14 ), child: SvgPicture.asset( fileState.getIcon(_imagePaths), @@ -65,14 +68,11 @@ class AttachmentFileComposerBuilder extends StatelessWidget with AppLoaderMixin ), title: Transform( transform: Matrix4.translationValues( - AppUtils.isDirectionRTL(context) ? 0.0 : BuildUtils.isWeb ? 0.0 : -8.0, - BuildUtils.isWeb ? -8.0 : -10.0, - 0.0), + DirectionUtils.isDirectionRTLByLanguage(context) ? 0.0 : (PlatformInfo.isWeb ? 0.0 : -8.0), + PlatformInfo.isWeb ? -8.0 : -10.0, + 0.0), child: Padding( - padding: EdgeInsets.only( - right: AppUtils.isDirectionRTL(context) ? 0 : BuildUtils.isWeb ? 20 : 16, - left: AppUtils.isDirectionRTL(context) ? BuildUtils.isWeb ? 20 : 16 : 0 - ), + padding: const EdgeInsetsDirectional.only(end: PlatformInfo.isWeb ? 20 : 16), child: Text( fileState.fileName, maxLines: 1, @@ -88,9 +88,9 @@ class AttachmentFileComposerBuilder extends StatelessWidget with AppLoaderMixin subtitle: fileState.fileSize != 0 ? Transform( transform: Matrix4.translationValues( - AppUtils.isDirectionRTL(context) ? 0.0 : BuildUtils.isWeb ? 0.0 : -8.0, - BuildUtils.isWeb ? -8.0 : -10.0, - 0.0), + DirectionUtils.isDirectionRTLByLanguage(context) ? 0.0 : PlatformInfo.isWeb ? 0.0 : -8.0, + PlatformInfo.isWeb ? -8.0 : -10.0, + 0.0), child: Text( filesize(fileState.fileSize), maxLines: 1, @@ -102,35 +102,20 @@ class AttachmentFileComposerBuilder extends StatelessWidget with AppLoaderMixin color: AppColor.colorContentEmail))) : null, ), - if (AppUtils.isDirectionRTL(context)) - Positioned( - left: BuildUtils.isWeb ? -5 : -12, - top: BuildUtils.isWeb ? -5 : -12, - child: buildIconWeb( - icon: SvgPicture.asset(_imagePaths.icDeleteAttachment, fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).delete, - onTap: () { - if (onDeleteAttachmentAction != null) { - onDeleteAttachmentAction!.call(fileState); - } + PositionedDirectional( + end: PlatformInfo.isWeb ? -5 : -12, + top: PlatformInfo.isWeb ? -5 : -12, + child: buildIconWeb( + icon: SvgPicture.asset(_imagePaths.icDeleteAttachment, fit: BoxFit.fill), + tooltip: AppLocalizations.of(context).delete, + onTap: () { + if (onDeleteAttachmentAction != null) { + onDeleteAttachmentAction!.call(fileState); } - ) + } ) - else - Positioned( - right: BuildUtils.isWeb ? -5 : -12, - top: BuildUtils.isWeb ? -5 : -12, - child: buildIconWeb( - icon: SvgPicture.asset(_imagePaths.icDeleteAttachment, fit: BoxFit.fill), - tooltip: AppLocalizations.of(context).delete, - onTap: () { - if (onDeleteAttachmentAction != null) { - onDeleteAttachmentAction!.call(fileState); - } - } - ) - ), - Align(alignment: Alignment.bottomCenter, child: _progressLoading), + ), + Align(alignment: AlignmentDirectional.bottomCenter, child: _progressLoading), ]), ) ); diff --git a/lib/features/composer/presentation/widgets/email_address_input_builder.dart b/lib/features/composer/presentation/widgets/email_address_input_builder.dart index 91e2cd02e..98d0d571a 100644 --- a/lib/features/composer/presentation/widgets/email_address_input_builder.dart +++ b/lib/features/composer/presentation/widgets/email_address_input_builder.dart @@ -13,6 +13,7 @@ import 'package:model/model.dart'; import 'package:super_tag_editor/tag_editor.dart'; import 'package:super_tag_editor/widgets/rich_text_widget.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_email_address_extension.dart'; +import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/suggestion_email_address.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/utils/app_constants.dart'; @@ -30,6 +31,7 @@ class EmailAddressInputBuilder { static const _suggestionBoxRadius = 20.0; final BuildContext _context; final ImagePaths _imagePaths; + final ResponsiveUtils _responsiveUtils; final ExpandMode expandMode; final PrefixEmailAddress _prefixEmailAddress; final List _listEmailAddressType; @@ -77,13 +79,14 @@ class EmailAddressInputBuilder { _onFocusEmailAddressChangeAction = onFocusEmailAddressChangeAction; } - void addOnFocusNextAddressAction(OnFocusNextAddressAction onFocusNextAddressAction) { + void addOnFocusNextAddressAction(OnFocusNextAddressAction? onFocusNextAddressAction) { _onFocusNextAddressAction = onFocusNextAddressAction; } EmailAddressInputBuilder( this._context, this._imagePaths, + this._responsiveUtils, this._prefixEmailAddress, this.listEmailAddress, this._listEmailAddressType, @@ -99,41 +102,55 @@ class EmailAddressInputBuilder { ); Widget build() { - return Row( - children: [ - Text('${_prefixEmailAddress.asName(_context)}:', - style: const TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput)), - const SizedBox(width: 8), - Expanded(child: Padding( - padding: EdgeInsets.only(right: _listEmailAddressType.length == 2 ? 8 : 8), - child: _buildTagEditor())), - if (_prefixEmailAddress == PrefixEmailAddress.to) - Row(children: [ - if (!_listEmailAddressType.contains(PrefixEmailAddress.cc)) - buildTextIcon(AppLocalizations.of(_context).cc_email_address_prefix, - padding: const EdgeInsets.all(5), - textStyle: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.normal, - decoration: TextDecoration.underline, - color: AppColor.lineItemListColor), - onTap: () => _onAddEmailAddressTypeAction?.call(PrefixEmailAddress.cc)), - if (!_listEmailAddressType.contains(PrefixEmailAddress.bcc)) - buildTextIcon(AppLocalizations.of(_context).bcc_email_address_prefix, + return Padding( + padding: _prefixEmailAddress == PrefixEmailAddress.to + ? ComposerStyle.getToAddressPadding(_context, _responsiveUtils) + : ComposerStyle.getCcBccAddressPadding(_context, _responsiveUtils), + child: Row( + children: [ + Text( + '${_prefixEmailAddress.asName(_context)}:', + style: const TextStyle( + fontSize: 15, + color: AppColor.colorHintEmailAddressInput + ) + ), + SizedBox(width: ComposerStyle.getSpace(_context, _responsiveUtils)), + Expanded(child: _buildTagEditor()), + SizedBox(width: ComposerStyle.getSpace(_context, _responsiveUtils)), + if (_prefixEmailAddress == PrefixEmailAddress.to) + Row(children: [ + if (!_listEmailAddressType.contains(PrefixEmailAddress.cc)) + buildTextIcon(AppLocalizations.of(_context).cc_email_address_prefix, padding: const EdgeInsets.all(5), textStyle: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.normal, - decoration: TextDecoration.underline, - color: AppColor.lineItemListColor), - onTap: () => _onAddEmailAddressTypeAction?.call(PrefixEmailAddress.bcc)), - const SizedBox(width: 10), - ]), - if (_prefixEmailAddress != PrefixEmailAddress.to) - buildIconWeb( - icon: SvgPicture.asset(_imagePaths.icCloseComposer, fit: BoxFit.fill), + fontSize: 15, + fontWeight: FontWeight.normal, + decoration: TextDecoration.underline, + color: AppColor.lineItemListColor), + onTap: () => _onAddEmailAddressTypeAction?.call(PrefixEmailAddress.cc)), + if (!_listEmailAddressType.contains(PrefixEmailAddress.bcc)) + buildTextIcon(AppLocalizations.of(_context).bcc_email_address_prefix, + padding: const EdgeInsets.all(5), + textStyle: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.normal, + decoration: TextDecoration.underline, + color: AppColor.lineItemListColor), + onTap: () => _onAddEmailAddressTypeAction?.call(PrefixEmailAddress.bcc)), + ]) + else + buildIconWeb( + icon: SvgPicture.asset( + _imagePaths.icCloseComposer, + width: 28, + height: 28, + fit: BoxFit.fill + ), + iconPadding: EdgeInsets.zero, onTap: () => _onDeleteEmailAddressTypeAction?.call(_prefixEmailAddress)) - ] + ] + ), ); } diff --git a/lib/features/composer/presentation/widgets/toolbar_rich_text_builder.dart b/lib/features/composer/presentation/widgets/toolbar_rich_text_builder.dart index f6ff4c944..ebfc74875 100644 --- a/lib/features/composer/presentation/widgets/toolbar_rich_text_builder.dart +++ b/lib/features/composer/presentation/widgets/toolbar_rich_text_builder.dart @@ -36,8 +36,8 @@ class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin final opacity = codeViewEnabled ? 0.5 : 1.0; return Container( - padding: padding ?? const EdgeInsets.only(left: 20, top: 8, bottom: 8), - alignment: Alignment.centerLeft, + padding: padding ?? const EdgeInsetsDirectional.only(start: 20, top: 8, bottom: 8), + alignment: AlignmentDirectional.centerStart, child: Wrap( crossAxisAlignment: WrapCrossAlignment.center, runSpacing: 8, @@ -93,7 +93,7 @@ class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin supportSelectionIcon: true)), ), Padding( - padding: const EdgeInsets.only(right: 4.0), + padding: const EdgeInsetsDirectional.only(end: 4.0), child: AbsorbPointer( absorbing: codeViewEnabled, child: buildWrapIconStyleText( @@ -106,7 +106,7 @@ class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin ), ), Padding( - padding: const EdgeInsets.only(right: 4.0), + padding: const EdgeInsetsDirectional.only(end: 4.0), child: AbsorbPointer( absorbing: codeViewEnabled, child: buildWrapIconStyleText( @@ -121,7 +121,7 @@ class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin ), ), Padding( - padding: const EdgeInsets.only(right: 4.0), + padding: const EdgeInsetsDirectional.only(end: 4.0), child: buildWrapIconStyleText( hasDropdown: false, padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 5), @@ -166,7 +166,7 @@ class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin ])), ), Padding( - padding: const EdgeInsets.only(right: 4.0), + padding: const EdgeInsetsDirectional.only(end: 4.0), child: AbsorbPointer( absorbing: codeViewEnabled, child: PopupMenuOverlayWidget( @@ -190,7 +190,7 @@ class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin ), ), Padding( - padding: const EdgeInsets.only(right: 4.0), + padding: const EdgeInsetsDirectional.only(end: 4.0), child: AbsorbPointer( absorbing: codeViewEnabled, child: PopupMenuOverlayWidget( diff --git a/pubspec.lock b/pubspec.lock index 74c3420da..010a4cb79 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -624,7 +624,7 @@ packages: source: hosted version: "1.1.0" flutter_keyboard_visibility: - dependency: "direct overridden" + dependency: "direct main" description: name: flutter_keyboard_visibility sha256: "4983655c26ab5b959252ee204c2fffa4afeb4413cd030455194ec0caa3b8e7cb" @@ -1471,10 +1471,11 @@ packages: super_tag_editor: dependency: "direct main" description: - name: super_tag_editor - sha256: f992eab1bad3595148f1a62f98fd981fac814d53682b8414311eca470dbfa768 - url: "https://pub.dev" - source: hosted + path: "." + ref: support_rtl + resolved-ref: d452d213992158bee12bef299364a4b3cc1fbc2b + url: "https://github.com/dab246/super_tag_editor.git" + source: git version: "0.1.2" syncfusion_flutter_core: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index 33adc13bc..e70226472 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -197,6 +197,8 @@ dependencies: flutter_typeahead: 4.6.0 + flutter_keyboard_visibility: 5.4.1 + dev_dependencies: flutter_test: sdk: flutter @@ -221,13 +223,16 @@ dependency_overrides: pointer_interceptor: 0.9.1 - flutter_keyboard_visibility: 5.4.1 - html_editor_enhanced: git: url: https://github.com/linagora/html-editor-enhanced.git ref: support_rtl + super_tag_editor: + git: + url: https://github.com/dab246/super_tag_editor.git + ref: support_rtl + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec