From 6d6cecc9cdaaf57dab9bc7cd4d0ff85d667a37e3 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 12 Jul 2023 16:10:49 +0700 Subject: [PATCH 1/5] TF-1486 Update size, padding for identity creator view --- .../presentation/identity_creator_view.dart | 326 ++++++++---------- ...set_default_identity_checkbox_builder.dart | 4 +- 2 files changed, 145 insertions(+), 185 deletions(-) diff --git a/lib/features/identity_creator/presentation/identity_creator_view.dart b/lib/features/identity_creator/presentation/identity_creator_view.dart index 5996ddcae6..dae98848b0 100644 --- a/lib/features/identity_creator/presentation/identity_creator_view.dart +++ b/lib/features/identity_creator/presentation/identity_creator_view.dart @@ -43,133 +43,120 @@ class IdentityCreatorView extends GetWidget { @override Widget build(BuildContext context) { return ResponsiveWidget( - responsiveUtils: _responsiveUtils, - mobile: Card( - margin: EdgeInsets.zero, - borderOnForeground: false, - color: Colors.transparent, - child: SafeArea( - top: PlatformInfo.isMobile, - bottom: false, - left: false, - right: false, - child: ClipRRect( - borderRadius: const BorderRadius.only( - topRight: Radius.circular(16), - topLeft: Radius.circular(16)), - child: GestureDetector( - onTap: () => controller.clearFocusEditor(context), - child: Scaffold( - backgroundColor: Colors.white, - body: Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topRight: Radius.circular(16), - topLeft: Radius.circular(16)), - boxShadow: [ - BoxShadow( - color: AppColor.colorShadowLayerBottom, - blurRadius: 96, - spreadRadius: 96, - offset: Offset.zero), - BoxShadow( - color: AppColor.colorShadowLayerTop, - blurRadius: 2, - spreadRadius: 2, - offset: Offset.zero), - ]), - child: _buildBodyMobile(context), - ), + responsiveUtils: _responsiveUtils, + mobile: Card( + margin: EdgeInsets.zero, + borderOnForeground: false, + color: Colors.transparent, + child: SafeArea( + top: PlatformInfo.isMobile, + bottom: false, + left: false, + right: false, + child: ClipRRect( + borderRadius: const BorderRadius.only( + topRight: Radius.circular(16), + topLeft: Radius.circular(16)), + child: GestureDetector( + onTap: () => controller.clearFocusEditor(context), + child: Scaffold( + backgroundColor: Colors.white, + body: Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topRight: Radius.circular(16), + topLeft: Radius.circular(16)), + boxShadow: [ + BoxShadow( + color: AppColor.colorShadowLayerBottom, + blurRadius: 96, + spreadRadius: 96, + offset: Offset.zero), + BoxShadow( + color: AppColor.colorShadowLayerTop, + blurRadius: 2, + spreadRadius: 2, + offset: Offset.zero), + ]), + child: _buildBodyView(context), ), ), ), ), ), - landscapeMobile: Scaffold( - backgroundColor: Colors.white, - body: GestureDetector( - onTap: () => controller.clearFocusEditor(context), - child: SafeArea( - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.zero), - child: _buildBodyMobile(context) + ), + landscapeMobile: Scaffold( + backgroundColor: Colors.white, + body: GestureDetector( + onTap: () => controller.clearFocusEditor(context), + child: SafeArea( + child: _buildBodyView(context), + ), + ) + ), + tablet: Scaffold( + backgroundColor: Colors.black38, + body: GestureDetector( + onTap: () => controller.clearFocusEditor(context), + child: Center( + child: Padding( + padding: const EdgeInsetsDirectional.symmetric(horizontal: 24), + child: Card( + 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: math.max(_responsiveUtils.getSizeScreenWidth(context) * 0.4, 700), + height: _responsiveUtils.getSizeScreenHeight(context) * 0.8, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(16)), + child: _buildBodyView(context) + ) + ) ), ) + ) ), - tablet: Scaffold( - backgroundColor: Colors.black38, - body: GestureDetector( - onTap: () => controller.clearFocusEditor(context), - child: Center(child: Card( - 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: _responsiveUtils.getSizeScreenWidth(context) * 0.85, - height: _responsiveUtils.getSizeScreenHeight(context) * 0.6, - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(16)), - child: _buildBodyMobile(context) - ) - ) - )) + ), + desktop: Scaffold( + backgroundColor: Colors.black38, + body: GestureDetector( + onTap: () => controller.clearFocusEditor(context), + child: Center(child: Card( + color: Colors.transparent, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(16)) ), - ), - landscapeTablet: Scaffold( - backgroundColor: Colors.black38, - body: GestureDetector( - onTap: () => controller.clearFocusEditor(context), - child: Center(child: Card( - 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: _responsiveUtils.getSizeScreenWidth(context) * 0.65, - height: _responsiveUtils.getSizeScreenHeight(context) * 0.8, - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(16)), - child: _buildBodyMobile(context) - ) - ) - )) - ) - ), - desktop: Scaffold( - backgroundColor: Colors.black38, - body: GestureDetector( - onTap: () => controller.clearFocusEditor(context), - child: Center(child: Card( - 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: math.max(_responsiveUtils.getSizeScreenWidth(context) * 0.4, 650), - height: _responsiveUtils.getSizeScreenHeight(context) * 0.75, - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(16)), - child: _buildBodyMobile(context) - ) - ) - )), + child: Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(16)) + ), + width: math.max(_responsiveUtils.getSizeScreenWidth(context) * 0.4, 800), + height: _responsiveUtils.getSizeScreenHeight(context) * 0.8, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(16)), + child: _buildBodyView(context) + ) ) + )), ) + ) ); } - Widget _buildBodyMobile(BuildContext context) { + Widget _buildBodyView(BuildContext context) { final bodyCreatorView = SingleChildScrollView( controller: controller.scrollController, physics: const ClampingScrollPhysics(), child: Padding( - padding: const EdgeInsets.all(24.0), + padding: const EdgeInsetsDirectional.symmetric(vertical: 12, horizontal: 24), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Obx(() => IdentityInputFieldBuilder( AppLocalizations.of(context).name, @@ -237,8 +224,10 @@ class IdentityCreatorView extends GetWidget { borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColor.colorInputBorderCreateMailbox), ), + padding: const EdgeInsetsDirectional.all(16), child: _buildSignatureHtmlTemplate(context), ), + const SizedBox(height: 12), if (_isMobile(context)) _buildActionButtonMobile(context) else @@ -309,15 +298,16 @@ class IdentityCreatorView extends GetWidget { Widget _buildSignatureHtmlTemplate(BuildContext context) { final htmlEditor = PlatformInfo.isWeb - ? _buildHtmlEditorWeb(context, controller.contentHtmlEditor ?? '') - : _buildHtmlEditor(context, initialContent: controller.contentHtmlEditor ?? ''); + ? _buildHtmlEditorWeb(context, controller.contentHtmlEditor ?? '') + : _buildHtmlEditor(context, initialContent: controller.contentHtmlEditor ?? ''); return Column( children: [ - if(PlatformInfo.isWeb) + if (PlatformInfo.isWeb) ToolbarRichTextWebBuilder( richTextWebController: controller.richTextWebController, - padding: const EdgeInsets.only(top: 22, bottom: 8.0, left: 24, right: 12)), + padding: const EdgeInsets.only(bottom: 12) + ), htmlEditor, ], ); @@ -325,102 +315,72 @@ class IdentityCreatorView extends GetWidget { Widget _buildHtmlEditorWeb(BuildContext context, String initContent) { log('IdentityCreatorView::_buildHtmlEditorWeb(): initContent: $initContent'); - return Padding( - padding: const EdgeInsets.only(left: 14.0, right: 2.0), - child: html_editor_browser.HtmlEditor( - key: const Key('identity_create_editor_web'), - controller: controller.richTextWebController.editorController, - htmlEditorOptions: html_editor_browser.HtmlEditorOptions( - hint: '', - darkMode: false, - customBodyCssStyle: HtmlUtils.customCssStyleHtmlEditor(direction: AppUtils.getCurrentDirection(context)), - ), - blockQuotedContent: initContent, - htmlToolbarOptions: const html_editor_browser.HtmlToolbarOptions( - toolbarType: html_editor_browser.ToolbarType.hide, - defaultToolbarButtons: []), - otherOptions: const html_editor_browser.OtherOptions(height: 150), - callbacks: html_editor_browser.Callbacks(onBeforeCommand: (currentHtml) { - log('IdentityCreatorView::_buildHtmlEditorWeb(): onBeforeCommand : $currentHtml'); - controller.updateContentHtmlEditor(currentHtml); - }, onChangeContent: (changed) { - log('IdentityCreatorView::_buildHtmlEditorWeb(): onChangeContent : $changed'); - controller.updateContentHtmlEditor(changed); - }, onInit: () { - log('IdentityCreatorView::_buildHtmlEditorWeb(): onInit'); + return html_editor_browser.HtmlEditor( + key: const Key('identity_create_editor_web'), + controller: controller.richTextWebController.editorController, + htmlEditorOptions: html_editor_browser.HtmlEditorOptions( + shouldEnsureVisible: true, + hint: '', + darkMode: false, + customBodyCssStyle: HtmlUtils.customCssStyleHtmlEditor(direction: AppUtils.getCurrentDirection(context)), + ), + blockQuotedContent: initContent, + htmlToolbarOptions: const html_editor_browser.HtmlToolbarOptions( + toolbarType: html_editor_browser.ToolbarType.hide, + defaultToolbarButtons: [] + ), + otherOptions: const html_editor_browser.OtherOptions(height: 200), + callbacks: html_editor_browser.Callbacks( + onBeforeCommand: controller.updateContentHtmlEditor, + onChangeContent: controller.updateContentHtmlEditor, + onInit: () { controller.richTextWebController.editorController.setFullScreen(); controller.updateContentHtmlEditor(initContent); }, onFocus: () { - log('IdentityCreatorView::_buildHtmlEditorWeb(): onFocus'); FocusManager.instance.primaryFocus?.unfocus(); Future.delayed(const Duration(milliseconds: 500), () { controller.richTextWebController.editorController.setFocus(); }); controller.richTextWebController.closeAllMenuPopup(); - }, onChangeSelection: (settings) { - controller.richTextWebController.onEditorSettingsChange(settings); - }, onChangeCodeview: (contentChanged) { - log('IdentityCreatorView::_buildHtmlEditorWeb(): onChangeCodeView : $contentChanged'); - controller.updateContentHtmlEditor(contentChanged); - }), + }, + onChangeSelection: controller.richTextWebController.onEditorSettingsChange, + onChangeCodeview: controller.updateContentHtmlEditor ), ); } Widget _buildHtmlEditor(BuildContext context, {String? initialContent}) { - return Padding( - padding: const EdgeInsets.all(16), - child: HtmlEditor( - key: controller.htmlKey, - minHeight: controller.htmlEditorMinHeight, - addDefaultSelectionMenuItems: false, - initialContent: initialContent ?? '', - customStyleCss: HtmlUtils.customCssStyleHtmlEditor(direction: AppUtils.getCurrentDirection(context)), - onCreated: (editorApi) => controller.initRichTextForMobile(context, editorApi), - ), + return HtmlEditor( + key: controller.htmlKey, + minHeight: controller.htmlEditorMinHeight, + addDefaultSelectionMenuItems: false, + initialContent: initialContent ?? '', + customStyleCss: HtmlUtils.customCssStyleHtmlEditor(direction: AppUtils.getCurrentDirection(context)), + onCreated: (editorApi) => controller.initRichTextForMobile(context, editorApi), ); } Widget _buildActionButtonDesktop(BuildContext context) { return Row( children: [ - _buildCheckboxIdentityDefault(context), - Expanded( - child: Padding( - padding: const EdgeInsetsDirectional.only( - top: 24.0, - bottom: 40.0, - start: 12, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - _buildCancelButton(context, width: 156), - const SizedBox(width: 12), - _buildSaveButton(context, width: 156), - ], - ), - ), - ) + Expanded(child: _buildCheckboxIdentityDefault(context)), + const SizedBox(width: 12), + _buildCancelButton(context, width: 156), + const SizedBox(width: 12), + _buildSaveButton(context, width: 156) ], ); } Widget _buildActionButtonMobile(BuildContext context) { return Column(children: [ - Padding( - padding: const EdgeInsets.only(top: 24), - child: _buildCheckboxIdentityDefault(context)), - Container( - alignment: Alignment.center, - color: Colors.white, - padding: const EdgeInsets.only(top: 24, bottom: 64), - child: Row(children: [ - Expanded(child: _buildCancelButton(context)), - const SizedBox(width: 12), - Expanded(child: _buildSaveButton(context)) - ]), - ) + _buildCheckboxIdentityDefault(context), + const SizedBox(height: 24), + Row(children: [ + Expanded(child: _buildCancelButton(context)), + const SizedBox(width: 12), + Expanded(child: _buildSaveButton(context)) + ]) ]); } diff --git a/lib/features/identity_creator/presentation/widgets/set_default_identity_checkbox_builder.dart b/lib/features/identity_creator/presentation/widgets/set_default_identity_checkbox_builder.dart index 18b9460b49..fa4e530c95 100644 --- a/lib/features/identity_creator/presentation/widgets/set_default_identity_checkbox_builder.dart +++ b/lib/features/identity_creator/presentation/widgets/set_default_identity_checkbox_builder.dart @@ -42,8 +42,8 @@ class SetDefaultIdentityCheckboxBuilder extends StatelessWidget { )), ), ), - Padding( - padding: const EdgeInsetsDirectional.only(start: 8), + const SizedBox(width: 8), + Flexible( child: Text( AppLocalizations.of(context).setDefaultIdentity, style: const TextStyle( From 3c895347adbc75f66c809b61d892ac68f77aeeee Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 12 Jul 2023 19:04:32 +0700 Subject: [PATCH 2/5] TF-1486 Add insert image in signature on web --- assets/images/ic_add_picture.svg | 5 ++ .../presentation/resources/image_paths.dart | 1 + .../controller/rich_text_web_controller.dart | 14 ++++++ .../widgets/toolbar_rich_text_builder.dart | 4 ++ .../extesions/size_extension.dart | 5 ++ .../identity_creator_controller.dart | 46 ++++++++++++++++++- .../presentation/identity_creator_view.dart | 25 +++++++--- .../utils/identity_creator_constants.dart | 4 ++ lib/l10n/intl_messages.arb | 12 ++++- lib/main/localizations/app_localizations.dart | 7 +++ 10 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 assets/images/ic_add_picture.svg create mode 100644 lib/features/identity_creator/presentation/extesions/size_extension.dart create mode 100644 lib/features/identity_creator/presentation/utils/identity_creator_constants.dart diff --git a/assets/images/ic_add_picture.svg b/assets/images/ic_add_picture.svg new file mode 100644 index 0000000000..9db70fafc6 --- /dev/null +++ b/assets/images/ic_add_picture.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index a58751a4b6..39bc83c9e6 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -193,6 +193,7 @@ class ImagePaths { String get icArrowBottom => _getImagePath('ic_arrow_bottom.svg'); String get icArrowLeft => _getImagePath('ic_arrow_left.svg'); String get icArrowRight => _getImagePath('ic_arrow_right.svg'); + String get icAddPicture => _getImagePath('ic_add_picture.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/lib/features/composer/presentation/controller/rich_text_web_controller.dart b/lib/features/composer/presentation/controller/rich_text_web_controller.dart index ccf47f7df3..b35b34c4bd 100644 --- a/lib/features/composer/presentation/controller/rich_text_web_controller.dart +++ b/lib/features/composer/presentation/controller/rich_text_web_controller.dart @@ -1,7 +1,10 @@ +import 'dart:convert'; + import 'package:core/presentation/extensions/color_extension.dart'; import 'package:custom_pop_up_menu/custom_pop_up_menu.dart'; import 'package:core/utils/app_logger.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:html_editor_enhanced/html_editor.dart'; @@ -257,6 +260,17 @@ class RichTextWebController extends BaseRichTextController { menuOrderListController.hideMenu(); } + void insertImageAsBase64({required PlatformFile platformFile}) { + if (platformFile.bytes != null) { + final base64Data = base64Encode(platformFile.bytes!); + editorController.insertHtml( + 'Image in my signature' + ); + } else { + logError("RichTextWebController::insertImageAsBase64: bytes is null"); + } + } + @override void onClose() { menuParagraphController.dispose(); 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 ebfc74875e..a696187720 100644 --- a/lib/features/composer/presentation/widgets/toolbar_rich_text_builder.dart +++ b/lib/features/composer/presentation/widgets/toolbar_rich_text_builder.dart @@ -22,11 +22,13 @@ class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin final RichTextWebController richTextWebController; final ImagePaths _imagePaths = Get.find(); final EdgeInsetsGeometry? padding; + final List? extendedOption; ToolbarRichTextWebBuilder({ Key? key, required this.richTextWebController, this.padding, + this.extendedOption, }) : super(key: key); @override @@ -42,6 +44,8 @@ class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin crossAxisAlignment: WrapCrossAlignment.center, runSpacing: 8, children: [ + if (extendedOption?.isNotEmpty == true) + ...extendedOption!, AbsorbPointer( absorbing: codeViewEnabled, child: DropDownMenuHeaderStyleWidget( diff --git a/lib/features/identity_creator/presentation/extesions/size_extension.dart b/lib/features/identity_creator/presentation/extesions/size_extension.dart new file mode 100644 index 0000000000..21c56f87d6 --- /dev/null +++ b/lib/features/identity_creator/presentation/extesions/size_extension.dart @@ -0,0 +1,5 @@ + +extension SizeExtension on int { + + int get kiloByteToBytes => this * 1024; +} \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/identity_creator_controller.dart b/lib/features/identity_creator/presentation/identity_creator_controller.dart index 97876538c4..2af63fcd4d 100644 --- a/lib/features/identity_creator/presentation/identity_creator_controller.dart +++ b/lib/features/identity_creator/presentation/identity_creator_controller.dart @@ -1,9 +1,10 @@ - +import 'package:core/presentation/utils/app_toast.dart'; import 'package:core/presentation/utils/keyboard_utils.dart'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; import 'package:core/utils/platform_info.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; @@ -20,7 +21,9 @@ import 'package:model/user/user_profile.dart'; import 'package:rich_text_composer/rich_text_composer.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/extesions/size_extension.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/model/identity_creator_arguments.dart'; +import 'package:tmail_ui_user/features/identity_creator/presentation/utils/identity_creator_constants.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/email_address_validator.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/empty_name_validator.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/state/verify_name_view_state.dart'; @@ -34,6 +37,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/extensions/id import 'package:tmail_ui_user/features/manage_account/presentation/model/identity_action_type.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:uuid/uuid.dart'; @@ -44,6 +48,7 @@ class IdentityCreatorController extends BaseController { final IdentityUtils _identityUtils; final _uuid = Get.find(); + final _appToast = Get.find(); final noneEmailAddress = EmailAddress(null, 'None'); final listEmailAddressDefault = [].obs; @@ -302,12 +307,14 @@ class IdentityCreatorController extends BaseController { final error = _getErrorInputNameString(context); if (error?.isNotEmpty == true) { errorNameIdentity.value = error; + inputNameIdentityFocusNode.requestFocus(); return; } final errorBcc = _getErrorInputAddressString(context); if (errorBcc?.isNotEmpty == true) { errorBccIdentity.value = errorBcc; + inputBccIdentityFocusNode.requestFocus(); return; } @@ -456,4 +463,41 @@ class IdentityCreatorController extends BaseController { ); } } + + void pickImage(BuildContext context) async { + final filePickerResult = await FilePicker.platform.pickFiles( + type: FileType.image, + withData: true + ); + + if (context.mounted) { + final platformFile = filePickerResult?.files.single; + if (platformFile != null) { + _insertInlineImage(context, platformFile); + } else { + _appToast.showToastErrorMessage( + context, + AppLocalizations.of(context).cannotSelectThisImage + ); + } + } else { + logError("IdentityCreatorController::pickImage: context is unmounted"); + } + } + + bool _isExceedMaxSizeInlineImage(int fileSize) => + fileSize > IdentityCreatorConstants.maxSizeIdentityInlineImage.kiloByteToBytes; + + void _insertInlineImage(BuildContext context, PlatformFile platformFile) { + if (_isExceedMaxSizeInlineImage(platformFile.size)) { + _appToast.showToastErrorMessage( + context, + AppLocalizations.of(context).pleaseChooseAnImageSizeCorrectly( + IdentityCreatorConstants.maxSizeIdentityInlineImage + ) + ); + } else { + richTextWebController.insertImageAsBase64(platformFile: platformFile); + } + } } \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/identity_creator_view.dart b/lib/features/identity_creator/presentation/identity_creator_view.dart index dae98848b0..b0dda39577 100644 --- a/lib/features/identity_creator/presentation/identity_creator_view.dart +++ b/lib/features/identity_creator/presentation/identity_creator_view.dart @@ -8,7 +8,6 @@ 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/responsive/responsive_widget.dart'; -import 'package:core/utils/app_logger.dart'; import 'package:core/utils/platform_info.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -18,6 +17,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:rich_text_composer/rich_text_composer.dart'; import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart'; +import 'package:tmail_ui_user/features/composer/presentation/mixin/rich_text_button_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/toolbar_rich_text_builder.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/identity_creator_controller.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/widgets/identity_drop_list_field_builder.dart'; @@ -30,7 +30,8 @@ import 'package:tmail_ui_user/features/manage_account/presentation/model/identit import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart'; -class IdentityCreatorView extends GetWidget { +class IdentityCreatorView extends GetWidget + with RichTextButtonMixin { final _imagePaths = Get.find(); final _responsiveUtils = Get.find(); @@ -163,7 +164,7 @@ class IdentityCreatorView extends GetWidget { controller.errorNameIdentity.value, AppLocalizations.of(context).required, editingController: controller.inputNameIdentityController, - focusNode: PlatformInfo.isWeb ? null : controller.inputNameIdentityFocusNode, + focusNode: controller.inputNameIdentityFocusNode, isMandatory: true, onChangeInputNameAction: (value) => controller.updateNameIdentity(context, value) )), @@ -195,7 +196,7 @@ class IdentityCreatorView extends GetWidget { AppLocalizations.of(context).bcc_to, controller.errorBccIdentity.value, controller.inputBccIdentityController, - focusNode: PlatformInfo.isWeb ? null : controller.inputBccIdentityFocusNode, + focusNode: controller.inputBccIdentityFocusNode, onSelectedSuggestionAction: (newEmailAddress) { controller.inputBccIdentityController.text = newEmailAddress?.email ?? ''; controller.updateBccOfIdentity(newEmailAddress); @@ -306,7 +307,20 @@ class IdentityCreatorView extends GetWidget { if (PlatformInfo.isWeb) ToolbarRichTextWebBuilder( richTextWebController: controller.richTextWebController, - padding: const EdgeInsets.only(bottom: 12) + padding: const EdgeInsets.only(bottom: 12), + extendedOption: [ + Padding( + padding: const EdgeInsetsDirectional.only(end: 4.0), + child: buildWrapIconStyleText( + icon: buildIconWithTooltip( + path: _imagePaths.icAddPicture, + tooltip: AppLocalizations.of(context).insertImage + ), + hasDropdown: false, + onTap: () => controller.pickImage(context) + ), + ), + ] ), htmlEditor, ], @@ -314,7 +328,6 @@ class IdentityCreatorView extends GetWidget { } Widget _buildHtmlEditorWeb(BuildContext context, String initContent) { - log('IdentityCreatorView::_buildHtmlEditorWeb(): initContent: $initContent'); return html_editor_browser.HtmlEditor( key: const Key('identity_create_editor_web'), controller: controller.richTextWebController.editorController, diff --git a/lib/features/identity_creator/presentation/utils/identity_creator_constants.dart b/lib/features/identity_creator/presentation/utils/identity_creator_constants.dart new file mode 100644 index 0000000000..8d7a6ccfb3 --- /dev/null +++ b/lib/features/identity_creator/presentation/utils/identity_creator_constants.dart @@ -0,0 +1,4 @@ + +class IdentityCreatorConstants { + static const int maxSizeIdentityInlineImage = 16; // Kilobyte +} \ No newline at end of file diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 6bb4b653eb..b5af622736 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-07-07T16:07:02.053606", + "@@last_modified": "2023-07-12T18:53:48.611802", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -2965,5 +2965,15 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "pleaseChooseAnImageSizeCorrectly": "Please choose an image size <= {maxSize}KB", + "@pleaseChooseAnImageSizeCorrectly": { + "type": "text", + "placeholders_order": [ + "maxSize" + ], + "placeholders": { + "maxSize": {} + } } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index a1f5d77997..98215ff52f 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -3064,4 +3064,11 @@ class AppLocalizations { name: 'archiveMailboxDisplayName', ); } + + String pleaseChooseAnImageSizeCorrectly(int maxSize) { + return Intl.message( + 'Please choose an image size <= ${maxSize}KB', + name: 'pleaseChooseAnImageSizeCorrectly', + args: [maxSize]); + } } \ No newline at end of file From 42daf7bd4d0966b094aac7a111928a15983ca33d Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 13 Jul 2023 00:25:12 +0700 Subject: [PATCH 3/5] TF-1486 Add insert image in signature on mobile/tablet --- .../extensions/color_extension.dart | 1 + .../text/type_ahead_form_field_builder.dart | 1 + .../rich_text_mobile_tablet_controller.dart | 14 +- .../extesions/size_extension.dart | 2 +- .../identity_creator_controller.dart | 35 ++++- .../presentation/identity_creator_view.dart | 120 ++++++++++-------- .../utils/identity_creator_constants.dart | 2 +- 7 files changed, 113 insertions(+), 62 deletions(-) diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index ba69f2ff4e..7bb309437f 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -147,6 +147,7 @@ extension AppColor on Color { static const colorCloseButton = Color(0xFF818C99); static const colorDropShadow = Color(0x0F000000); static const colorBackgroundKeyboard = Color(0xFFD2D5DC); + static const colorBackgroundKeyboardAndroid = Color(0xFFF2F0F4); static const colorShadowLayerBottom = Color(0x29000000); static const colorShadowLayerTop = Color(0x1F000000); static const colorDividerHorizontal = Color(0x1F000000); diff --git a/core/lib/presentation/views/text/type_ahead_form_field_builder.dart b/core/lib/presentation/views/text/type_ahead_form_field_builder.dart index 7fa7363a54..01e64d93c9 100644 --- a/core/lib/presentation/views/text/type_ahead_form_field_builder.dart +++ b/core/lib/presentation/views/text/type_ahead_form_field_builder.dart @@ -74,6 +74,7 @@ class _TypeAheadFormFieldBuilderState extends State this * 1024; + int get toBytes => this * 1024; } \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/identity_creator_controller.dart b/lib/features/identity_creator/presentation/identity_creator_controller.dart index 2af63fcd4d..61483c8caa 100644 --- a/lib/features/identity_creator/presentation/identity_creator_controller.dart +++ b/lib/features/identity_creator/presentation/identity_creator_controller.dart @@ -20,6 +20,7 @@ import 'package:model/extensions/identity_extension.dart'; import 'package:model/user/user_profile.dart'; import 'package:rich_text_composer/rich_text_composer.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/extesions/size_extension.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/model/identity_creator_arguments.dart'; @@ -61,8 +62,10 @@ class IdentityCreatorController extends BaseController { final actionType = IdentityActionType.create.obs; final isDefaultIdentity = RxBool(false); final isDefaultIdentitySupported = RxBool(false); + final isMobileEditorFocus = RxBool(false); final RichTextController keyboardRichTextController = RichTextController(); + final RichTextMobileTabletController richTextMobileTabletController = RichTextMobileTabletController(); final RichTextWebController richTextWebController = RichTextWebController(); final TextEditingController inputNameIdentityController = TextEditingController(); final TextEditingController inputBccIdentityController = TextEditingController(); @@ -79,7 +82,7 @@ class IdentityCreatorController extends BaseController { IdentityCreatorArguments? arguments; final GlobalKey htmlKey = GlobalKey(); - final htmlEditorMinHeight = 160; + final htmlEditorMinHeight = 150; void updateNameIdentity(BuildContext context, String? value) { _nameIdentity = value; @@ -428,17 +431,23 @@ class IdentityCreatorController extends BaseController { } void initRichTextForMobile(BuildContext context, HtmlEditorApi editorApi) { + richTextMobileTabletController.htmlEditorApi = editorApi; keyboardRichTextController.onCreateHTMLEditor( editorApi, onEnterKeyDown: _onEnterKeyDownOnMobile, onFocus: _onFocusHTMLEditorOnMobile, context: context ); + keyboardRichTextController.htmlEditorApi?.onFocusOut = () { + keyboardRichTextController.hideRichTextView(); + isMobileEditorFocus.value = false; + }; } void _onFocusHTMLEditorOnMobile() async { inputBccIdentityFocusNode.unfocus(); inputNameIdentityFocusNode.unfocus(); + isMobileEditorFocus.value = true; if (htmlKey.currentContext != null) { await Scrollable.ensureVisible(htmlKey.currentContext!); } @@ -464,7 +473,9 @@ class IdentityCreatorController extends BaseController { } } - void pickImage(BuildContext context) async { + void pickImage(BuildContext context, {int? maxWidth}) async { + clearFocusEditor(context); + final filePickerResult = await FilePicker.platform.pickFiles( type: FileType.image, withData: true @@ -473,7 +484,7 @@ class IdentityCreatorController extends BaseController { if (context.mounted) { final platformFile = filePickerResult?.files.single; if (platformFile != null) { - _insertInlineImage(context, platformFile); + _insertInlineImage(context, platformFile, maxWidth: maxWidth); } else { _appToast.showToastErrorMessage( context, @@ -486,18 +497,28 @@ class IdentityCreatorController extends BaseController { } bool _isExceedMaxSizeInlineImage(int fileSize) => - fileSize > IdentityCreatorConstants.maxSizeIdentityInlineImage.kiloByteToBytes; + fileSize > IdentityCreatorConstants.maxKBSizeIdentityInlineImage.toBytes; - void _insertInlineImage(BuildContext context, PlatformFile platformFile) { + void _insertInlineImage( + BuildContext context, + PlatformFile platformFile, + {int? maxWidth} + ) { if (_isExceedMaxSizeInlineImage(platformFile.size)) { _appToast.showToastErrorMessage( context, AppLocalizations.of(context).pleaseChooseAnImageSizeCorrectly( - IdentityCreatorConstants.maxSizeIdentityInlineImage + IdentityCreatorConstants.maxKBSizeIdentityInlineImage ) ); } else { - richTextWebController.insertImageAsBase64(platformFile: platformFile); + if (PlatformInfo.isWeb) { + richTextWebController.insertImageAsBase64(platformFile: platformFile); + } else if (PlatformInfo.isMobile) { + richTextMobileTabletController.insertImageAsBase64(platformFile: platformFile, maxWidth: maxWidth); + } else { + logError("IdentityCreatorController::_insertInlineImage: Platform not supported"); + } } } } \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/identity_creator_view.dart b/lib/features/identity_creator/presentation/identity_creator_view.dart index b0dda39577..f195047163 100644 --- a/lib/features/identity_creator/presentation/identity_creator_view.dart +++ b/lib/features/identity_creator/presentation/identity_creator_view.dart @@ -43,43 +43,33 @@ class IdentityCreatorView extends GetWidget @override Widget build(BuildContext context) { - return ResponsiveWidget( + final responsiveWidget = ResponsiveWidget( responsiveUtils: _responsiveUtils, - mobile: Card( - margin: EdgeInsets.zero, - borderOnForeground: false, - color: Colors.transparent, - child: SafeArea( - top: PlatformInfo.isMobile, - bottom: false, - left: false, - right: false, - child: ClipRRect( - borderRadius: const BorderRadius.only( - topRight: Radius.circular(16), - topLeft: Radius.circular(16)), - child: GestureDetector( - onTap: () => controller.clearFocusEditor(context), - child: Scaffold( - backgroundColor: Colors.white, - body: Container( + mobile: Scaffold( + backgroundColor: Colors.black38, + body: GestureDetector( + onTap: () => controller.clearFocusEditor(context), + child: Card( + margin: EdgeInsets.zero, + borderOnForeground: false, + color: Colors.transparent, + child: SafeArea( + top: PlatformInfo.isMobile, + bottom: false, + left: false, + right: false, + child: ClipRRect( + borderRadius: const BorderRadius.only( + topRight: Radius.circular(16), + topLeft: Radius.circular(16)), + child: Container( decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( topRight: Radius.circular(16), - topLeft: Radius.circular(16)), - boxShadow: [ - BoxShadow( - color: AppColor.colorShadowLayerBottom, - blurRadius: 96, - spreadRadius: 96, - offset: Offset.zero), - BoxShadow( - color: AppColor.colorShadowLayerTop, - blurRadius: 2, - spreadRadius: 2, - offset: Offset.zero), - ]), + topLeft: Radius.circular(16) + ), + ), child: _buildBodyView(context), ), ), @@ -150,6 +140,29 @@ class IdentityCreatorView extends GetWidget ) ) ); + + if (PlatformInfo.isWeb) { + return responsiveWidget; + } else { + return KeyboardRichText( + keyBroadToolbar: RichTextKeyboardToolBar( + titleBack: AppLocalizations.of(context).titleFormat, + backgroundKeyboardToolBarColor: PlatformInfo.isIOS + ? AppColor.colorBackgroundKeyboard + : AppColor.colorBackgroundKeyboardAndroid, + isLandScapeMode: _responsiveUtils.isLandscapeMobile(context), + richTextController: controller.keyboardRichTextController, + titleQuickStyleBottomSheet: AppLocalizations.of(context).titleQuickStyles, + titleBackgroundBottomSheet: AppLocalizations.of(context).titleBackground, + titleForegroundBottomSheet: AppLocalizations.of(context).titleForeground, + titleFormatBottomSheet: AppLocalizations.of(context).titleFormat, + insertImage: () => controller.pickImage(context, maxWidth: _getMaxWidth(context).toInt()), + ), + richTextController: controller.keyboardRichTextController, + paddingChild: EdgeInsets.zero, + child: responsiveWidget + ); + } } Widget _buildBodyView(BuildContext context) { @@ -232,7 +245,15 @@ class IdentityCreatorView extends GetWidget if (_isMobile(context)) _buildActionButtonMobile(context) else - _buildActionButtonDesktop(context) + _buildActionButtonDesktop(context), + if (PlatformInfo.isMobile) + Obx(() { + if (controller.isMobileEditorFocus.isTrue) { + return const SizedBox(height: 48); + } else { + return const SizedBox.shrink(); + } + }) ]), ), ); @@ -241,24 +262,7 @@ class IdentityCreatorView extends GetWidget onTap: () => controller.clearFocusEditor(context), child: Column(children: [ _buildHeaderView(context), - Expanded( - child: PlatformInfo.isWeb - ? PointerInterceptor(child: bodyCreatorView) - : KeyboardRichText( - keyBroadToolbar: RichTextKeyboardToolBar( - titleBack: AppLocalizations.of(context).titleFormat, - backgroundKeyboardToolBarColor: AppColor.colorBackgroundKeyboard, - isLandScapeMode: _responsiveUtils.isLandscapeMobile(context), - richTextController: controller.keyboardRichTextController, - titleQuickStyleBottomSheet: AppLocalizations.of(context).titleQuickStyles, - titleBackgroundBottomSheet: AppLocalizations.of(context).titleBackground, - titleForegroundBottomSheet: AppLocalizations.of(context).titleForeground, - titleFormatBottomSheet: AppLocalizations.of(context).titleFormat, - ), - richTextController: controller.keyboardRichTextController, - paddingChild: EdgeInsets.zero, - child: bodyCreatorView), - ), + Expanded(child: PointerInterceptor(child: bodyCreatorView)) ]), ); } @@ -455,5 +459,17 @@ class IdentityCreatorView extends GetWidget )); } - bool _isMobile(BuildContext context) => _responsiveUtils.isPortraitMobile(context) || _responsiveUtils.isLandscapeMobile(context); + bool _isMobile(BuildContext context) => + _responsiveUtils.isPortraitMobile(context) || + _responsiveUtils.isLandscapeMobile(context); + + double _getMaxWidth(BuildContext context) { + if (_isMobile(context)) { + return _responsiveUtils.getSizeScreenWidth(context); + } else if (_responsiveUtils.isDesktop(context)) { + return math.max(_responsiveUtils.getSizeScreenWidth(context) * 0.4, 800); + } else { + return math.max(_responsiveUtils.getSizeScreenWidth(context) * 0.4, 700); + } + } } \ No newline at end of file diff --git a/lib/features/identity_creator/presentation/utils/identity_creator_constants.dart b/lib/features/identity_creator/presentation/utils/identity_creator_constants.dart index 8d7a6ccfb3..e26a3b27ab 100644 --- a/lib/features/identity_creator/presentation/utils/identity_creator_constants.dart +++ b/lib/features/identity_creator/presentation/utils/identity_creator_constants.dart @@ -1,4 +1,4 @@ class IdentityCreatorConstants { - static const int maxSizeIdentityInlineImage = 16; // Kilobyte + static const int maxKBSizeIdentityInlineImage = 16; // Kilobyte } \ No newline at end of file From c945333d677e1997b71f47c504252d1d431cc34e Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 13 Jul 2023 00:44:38 +0700 Subject: [PATCH 4/5] TF-1486 Change background rich text keyboard on android --- lib/features/composer/presentation/composer_view.dart | 9 +++++++-- .../presentation/vacation/vacation_view.dart | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index 2c09da9de7..ec2c3c8f11 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -5,6 +5,7 @@ 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/responsive/responsive_widget.dart'; +import 'package:core/utils/platform_info.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -49,7 +50,9 @@ class ComposerView extends BaseComposerView { return KeyboardRichText( richTextController: controller.keyboardRichTextController, keyBroadToolbar: RichTextKeyboardToolBar( - backgroundKeyboardToolBarColor: AppColor.colorBackgroundKeyboard, + backgroundKeyboardToolBarColor: PlatformInfo.isIOS + ? AppColor.colorBackgroundKeyboard + : AppColor.colorBackgroundKeyboardAndroid, isLandScapeMode: responsiveUtils.isLandscapeMobile(context), insertAttachment: controller.isNetworkConnectionAvailable ? () => controller.openPickAttachmentMenu(context, _pickAttachmentsActionTiles(context)) @@ -98,7 +101,9 @@ class ComposerView extends BaseComposerView { return KeyboardRichText( richTextController: controller.keyboardRichTextController, keyBroadToolbar: RichTextKeyboardToolBar( - backgroundKeyboardToolBarColor: AppColor.colorBackgroundKeyboard, + backgroundKeyboardToolBarColor: PlatformInfo.isIOS + ? AppColor.colorBackgroundKeyboard + : AppColor.colorBackgroundKeyboardAndroid, insertAttachment: () => controller.openPickAttachmentMenu(context, _pickAttachmentsActionTiles(context)), insertImage: () => controller.insertImage(context, constraints.maxWidth), richTextController: controller.keyboardRichTextController, diff --git a/lib/features/manage_account/presentation/vacation/vacation_view.dart b/lib/features/manage_account/presentation/vacation/vacation_view.dart index e04d4df6e4..3ac19a7463 100644 --- a/lib/features/manage_account/presentation/vacation/vacation_view.dart +++ b/lib/features/manage_account/presentation/vacation/vacation_view.dart @@ -36,7 +36,9 @@ class VacationView extends GetWidget with RichTextButtonMixi richTextController: controller.richTextControllerForMobile, keyBroadToolbar: RichTextKeyboardToolBar( titleBack: AppLocalizations.of(context).format, - backgroundKeyboardToolBarColor: AppColor.colorBackgroundKeyboard, + backgroundKeyboardToolBarColor: PlatformInfo.isIOS + ? AppColor.colorBackgroundKeyboard + : AppColor.colorBackgroundKeyboardAndroid, titleFormatBottomSheet: AppLocalizations.of(context).format, richTextController: controller.richTextControllerForMobile, titleQuickStyleBottomSheet: AppLocalizations.of(context).quickStyles, From 27e4e484575338bec0bf324ef99ff76c6654e0c2 Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 13 Jul 2023 00:49:13 +0700 Subject: [PATCH 5/5] TF-1486 Fix duplicate cursor in vacation on android --- .../presentation/vacation/vacation_controller.dart | 4 ++++ .../manage_account/presentation/vacation/vacation_view.dart | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/features/manage_account/presentation/vacation/vacation_controller.dart b/lib/features/manage_account/presentation/vacation/vacation_controller.dart index 3484781ec7..a19ebc27c3 100644 --- a/lib/features/manage_account/presentation/vacation/vacation_controller.dart +++ b/lib/features/manage_account/presentation/vacation/vacation_controller.dart @@ -38,6 +38,7 @@ class VacationController extends BaseController { final errorMessageBody = Rxn(); final subjectTextController = TextEditingController(); + final subjectTextFocusNode = FocusNode(); final richTextControllerForMobile = RichTextController(); final htmlEditorMinHeight = 150; @@ -338,6 +339,8 @@ class VacationController extends BaseController { } void onFocusHTMLEditor() async { + subjectTextFocusNode.unfocus(); + await Scrollable.ensureVisible(htmlKey.currentContext!); await Future.delayed(const Duration(milliseconds: 500), () { scrollController.animateTo( @@ -360,6 +363,7 @@ class VacationController extends BaseController { @override void onClose() { + subjectTextFocusNode.dispose(); subjectTextController.dispose(); richTextControllerForMobile.dispose(); scrollController.dispose(); diff --git a/lib/features/manage_account/presentation/vacation/vacation_view.dart b/lib/features/manage_account/presentation/vacation/vacation_view.dart index 3ac19a7463..769e99e715 100644 --- a/lib/features/manage_account/presentation/vacation/vacation_view.dart +++ b/lib/features/manage_account/presentation/vacation/vacation_view.dart @@ -251,7 +251,8 @@ class VacationView extends GetWidget with RichTextButtonMixi child: TextInputFieldBuilder( label: AppLocalizations.of(context).subject, hint: AppLocalizations.of(context).hintSubjectInputVacationSetting, - editingController: controller.subjectTextController + editingController: controller.subjectTextController, + focusNode: controller.subjectTextFocusNode, ), ) : Row(children: [ @@ -260,7 +261,8 @@ class VacationView extends GetWidget with RichTextButtonMixi child: TextInputFieldBuilder( label: AppLocalizations.of(context).subject, hint: AppLocalizations.of(context).hintSubjectInputVacationSetting, - editingController: controller.subjectTextController + editingController: controller.subjectTextController, + focusNode: controller.subjectTextFocusNode, ), )), const SizedBox(width: 24),