From e7429b9fd5ab98b9d6ecd9ac3fea2959628df7c7 Mon Sep 17 00:00:00 2001 From: ManhNTX Date: Fri, 30 Sep 2022 12:55:45 +0700 Subject: [PATCH] TF-912: Support rich text for signature `htmlBody` of identity --- .../composer/presentation/composer_view.dart | 53 +++--- .../identity_creator_controller.dart | 4 + .../presentation/identity_creator_view.dart | 178 ++++++++++-------- lib/l10n/intl_messages.arb | 26 ++- pubspec.yaml | 7 +- 5 files changed, 161 insertions(+), 107 deletions(-) diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index c9117ec31..25ad37504 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -291,6 +291,7 @@ class ComposerView extends GetWidget onFocusChange: (focus) { if(focus) { controller.htmlEditorApi?.unfocus(); + controller.keyboardRichTextController.hideRichTextView(); } }, child: Column( @@ -362,28 +363,36 @@ class ComposerView extends GetWidget } Widget _buildSubjectEmail(BuildContext context) { - return Row( - children: [ - Padding( - padding: const EdgeInsets.only(right: 8), - child: Text( - '${AppLocalizations.of(context).subject_email}:', - style: const TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))), - Expanded( - child: FocusScope(child: Focus( - onFocusChange: (focus) => controller.onSubjectEmailFocusChange(focus), - child: (TextFieldBuilder() - ..key(const Key('subject_email_input')) - ..cursorColor(AppColor.colorTextButton) - ..maxLines(responsiveUtils.isMobile(context) ? null : 1) - ..onChange((value) => controller.setSubjectEmail(value)) - ..textStyle(const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal)) - ..textDecoration(const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none)) - ..addController(controller.subjectEmailInputController)) - .build(), - )) - ) - ] + return Focus( + onFocusChange: (focus) { + if(focus) { + controller.htmlEditorApi?.unfocus(); + controller.keyboardRichTextController.hideRichTextView(); + } + }, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 8), + child: Text( + '${AppLocalizations.of(context).subject_email}:', + style: const TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))), + Expanded( + child: FocusScope(child: Focus( + onFocusChange: (focus) => controller.onSubjectEmailFocusChange(focus), + child: (TextFieldBuilder() + ..key(const Key('subject_email_input')) + ..cursorColor(AppColor.colorTextButton) + ..maxLines(responsiveUtils.isMobile(context) ? null : 1) + ..onChange((value) => controller.setSubjectEmail(value)) + ..textStyle(const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal)) + ..textDecoration(const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none)) + ..addController(controller.subjectEmailInputController)) + .build(), + )) + ) + ] + ), ); } diff --git a/lib/features/identity_creator/presentation/identity_creator_controller.dart b/lib/features/identity_creator/presentation/identity_creator_controller.dart index 0df3cc457..4c2692fde 100644 --- a/lib/features/identity_creator/presentation/identity_creator_controller.dart +++ b/lib/features/identity_creator/presentation/identity_creator_controller.dart @@ -11,6 +11,7 @@ import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:model/model.dart'; +import 'package:rich_text_composer/richtext_controller.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/model/signature_type.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/email_address_validator.dart'; @@ -46,6 +47,8 @@ class IdentityCreatorController extends BaseController { final _identitiesController = Get.find(); final _accountDashBoardController = Get.find(); + final RichTextController keyboardRichTextController = RichTextController(); + final HtmlEditorController signatureHtmlEditorController = HtmlEditorController(processNewLineAsBr: true); final TextEditingController signaturePlainEditorController = TextEditingController(); final TextEditingController inputNameIdentityController = TextEditingController(); @@ -102,6 +105,7 @@ class IdentityCreatorController extends BaseController { inputNameIdentityController.dispose(); inputBccIdentityController.dispose(); identityCreatorIsActiveWorker.dispose(); + keyboardRichTextController.dispose(); super.onClose(); } diff --git a/lib/features/identity_creator/presentation/identity_creator_view.dart b/lib/features/identity_creator/presentation/identity_creator_view.dart index eaa6b716a..c0c0145a9 100644 --- a/lib/features/identity_creator/presentation/identity_creator_view.dart +++ b/lib/features/identity_creator/presentation/identity_creator_view.dart @@ -5,6 +5,8 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:html_editor_enhanced/html_editor.dart' as html_editor_browser; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; +import 'package:rich_text_composer/views/keyboard_richtext.dart'; +import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/identity_creator_controller.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/model/signature_type.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/widgets/identity_drop_list_field_builder.dart'; @@ -279,28 +281,29 @@ class IdentityCreatorView extends GetWidget { : AppLocalizations.of(context).edit_identity.inCaps, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 21, color: Colors.black))), const SizedBox(height: 8), - Expanded(child: SingleChildScrollView( - physics: const ClampingScrollPhysics(), - reverse: true, - child: Padding( + Expanded(child: KeyboardRichText( + child: SingleChildScrollView( + physics: const ClampingScrollPhysics(), + reverse: true, + child: Padding( padding: const EdgeInsets.all(24.0), child: Column(children: [ Obx(() => (IdentityInputFieldBuilder( - AppLocalizations.of(context).name, - controller.errorNameIdentity.value, - editingController: controller.inputNameIdentityController, - focusNode: controller.inputNameIdentityFocusNode, - isMandatory: true) - ..addOnChangeInputNameAction((value) => controller.updateNameIdentity(context, value))) - .build()), + AppLocalizations.of(context).name, + controller.errorNameIdentity.value, + editingController: controller.inputNameIdentityController, + focusNode: controller.inputNameIdentityFocusNode, + isMandatory: true) + ..addOnChangeInputNameAction((value) => controller.updateNameIdentity(context, value))) + .build()), const SizedBox(height: 24), Obx(() { if (controller.actionType == IdentityActionType.create) { return (IdentityDropListFieldBuilder( - _imagePaths, - AppLocalizations.of(context).email.inCaps, - controller.emailOfIdentity.value, - controller.listEmailAddressDefault) + _imagePaths, + AppLocalizations.of(context).email.inCaps, + controller.emailOfIdentity.value, + controller.listEmailAddressDefault) ..addOnSelectEmailAddressDropListAction((emailAddress) => controller.updateEmailOfIdentity(emailAddress)) ).build(); @@ -313,40 +316,42 @@ class IdentityCreatorView extends GetWidget { }), const SizedBox(height: 24), Obx(() => (IdentityDropListFieldBuilder( - _imagePaths, - AppLocalizations.of(context).reply_to_address, - controller.replyToOfIdentity.value, - controller.listEmailAddressOfReplyTo) - ..addOnSelectEmailAddressDropListAction((newEmailAddress) => - controller.updaterReplyToOfIdentity(newEmailAddress))) - .build()), + _imagePaths, + AppLocalizations.of(context).reply_to_address, + controller.replyToOfIdentity.value, + controller.listEmailAddressOfReplyTo) + ..addOnSelectEmailAddressDropListAction((newEmailAddress) => + controller.updaterReplyToOfIdentity(newEmailAddress))) + .build()), const SizedBox(height: 24), Obx(() => (IdentityInputWithDropListFieldBuilder( - AppLocalizations.of(context).bcc_to_address, - controller.errorBccIdentity.value, - controller.inputBccIdentityController) - ..addOnSelectedSuggestionAction((newEmailAddress) { - controller.inputBccIdentityController.text = newEmailAddress?.email ?? ''; - controller.updateBccOfIdentity(newEmailAddress); - }) - ..addOnChangeInputSuggestionAction((pattern) { - controller.validateInputBccAddress(context, pattern); - if (pattern == null || pattern.trim().isEmpty) { - controller.updateBccOfIdentity(null); - } else { - controller.updateBccOfIdentity(EmailAddress(null, pattern)); - } - }) - ..addOnSuggestionCallbackAction((pattern) => - controller.getSuggestionEmailAddress(pattern))) - .build() + AppLocalizations.of(context).bcc_to_address, + controller.errorBccIdentity.value, + controller.inputBccIdentityController) + ..addOnSelectedSuggestionAction((newEmailAddress) { + controller.inputBccIdentityController.text = newEmailAddress?.email ?? ''; + controller.updateBccOfIdentity(newEmailAddress); + }) + ..addOnChangeInputSuggestionAction((pattern) { + controller.validateInputBccAddress(context, pattern); + if (pattern == null || pattern.trim().isEmpty) { + controller.updateBccOfIdentity(null); + } else { + controller.updateBccOfIdentity(EmailAddress(null, pattern)); + } + }) + ..addOnSuggestionCallbackAction((pattern) => + controller.getSuggestionEmailAddress(pattern))) + .build() ), const SizedBox(height: 32), Text(AppLocalizations.of(context).signature, - style: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - color: AppColor.colorContentEmail)), + style: const TextStyle( + fontWeight: FontWeight.normal, + fontSize: 14, + color: AppColor.colorContentEmail, + ), + ), const SizedBox(height: 8), Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Obx(() => _buildSignatureButton(context, SignatureType.plainText)), @@ -356,17 +361,18 @@ class IdentityCreatorView extends GetWidget { const SizedBox(height: 8), Obx(() => Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - border: Border.all(color: AppColor.colorInputBorderCreateMailbox), - color: Colors.white), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: AppColor.colorInputBorderCreateMailbox), + color: Colors.white, + ), padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), child: Stack( - children: [ - if (controller.signatureType.value == SignatureType.plainText) - _buildSignaturePlainTextTemplate(context) - else - _buildSignatureHtmlTemplate(context) - ] + children: [ + if (controller.signatureType.value == SignatureType.plainText) + _buildSignaturePlainTextTemplate(context) + else + _buildSignatureHtmlTemplate(context) + ] ), )), const SizedBox(height: 24), @@ -374,35 +380,48 @@ class IdentityCreatorView extends GetWidget { alignment: Alignment.center, color: Colors.white, child: Row( - children: [ - Expanded( - child: buildTextButton( - AppLocalizations.of(context).cancel, - textStyle: const TextStyle( - fontWeight: FontWeight.w500, - fontSize: 17, - color: AppColor.colorTextButton), - backgroundColor: AppColor.emailAddressChipColor, - width: 128, - height: 44, - radius: 10, - onTap: () => controller.closeView(context)), + children: [ + Expanded( + child: buildTextButton( + AppLocalizations.of(context).cancel, + textStyle: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 17, + color: AppColor.colorTextButton, + ), + backgroundColor: AppColor.emailAddressChipColor, + width: 128, + height: 44, + radius: 10, + onTap: () => controller.closeView(context), ), - const SizedBox(width: 12), - Expanded( - child: Obx(() => buildTextButton( - controller.actionType == IdentityActionType.create + ), + const SizedBox(width: 12), + Expanded( + child: Obx(() => buildTextButton( + controller.actionType == IdentityActionType.create ? AppLocalizations.of(context).create : AppLocalizations.of(context).save, - width: 128, - height: 44, - radius: 10, - onTap: () => controller.createNewIdentity(context))), - ), - ] + width: 128, + height: 44, + radius: 10, + onTap: () => controller.createNewIdentity(context))), + ), + ] ), ) - ]), + ]), + ), + ), + richTextController: controller.keyboardRichTextController, + backgroundKeyboardToolBarColor: AppColor.colorBackgroundKeyboard, + keyBroadToolbar: RichTextKeyboardToolBar( + 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, ), )), ]), @@ -492,7 +511,10 @@ class IdentityCreatorView extends GetWidget { return html_editor_mobile.HtmlEditor( key: const Key('signature_html_editor_mobile'), minHeight: 230, - onCreated: (htmlEditorController) => controller.signatureHtmlEditorMobileController = htmlEditorController, + onCreated: (htmlEditorController) { + controller.keyboardRichTextController.onCreateHTMLEditor(htmlEditorController); + controller.signatureHtmlEditorMobileController = htmlEditorController; + }, initialContent: controller.contentHtmlEditor ?? '', ); } diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 671dff626..c9935fec8 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-09-30T16:41:42.005040", + "@@last_modified": "2022-09-30T17:54:08.425416", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -2221,5 +2221,29 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "titleFormat": "Format", + "@titleFormat": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "titleQuickStyles": "Quick styles", + "@titleQuickStyles": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "titleBackground": "Background", + "@titleBackground": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "titleForeground": "Foreground", + "@titleForeground": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 246ba72c3..e698da906 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,7 +45,7 @@ dependencies: rich_text_composer: git: url: https://github.com/linagora/rich-text-composer.git - ref: apply_rich_text_bottom_sheet + ref: master # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 @@ -143,11 +143,6 @@ dependencies: url: https://github.com/linagora/html-editor-enhanced.git ref: email_supported - rich_text_composer: - git: - url: https://github.com/linagora/rich-text-composer.git - ref: master - # fk_user_agent fk_user_agent: 2.1.0