TF-912: Support rich text for signature htmlBody of identity

This commit is contained in:
ManhNTX
2022-09-30 12:55:45 +07:00
committed by Dat H. Pham
parent 00f781b67f
commit e7429b9fd5
5 changed files with 161 additions and 107 deletions
@@ -291,6 +291,7 @@ class ComposerView extends GetWidget<ComposerController>
onFocusChange: (focus) { onFocusChange: (focus) {
if(focus) { if(focus) {
controller.htmlEditorApi?.unfocus(); controller.htmlEditorApi?.unfocus();
controller.keyboardRichTextController.hideRichTextView();
} }
}, },
child: Column( child: Column(
@@ -362,28 +363,36 @@ class ComposerView extends GetWidget<ComposerController>
} }
Widget _buildSubjectEmail(BuildContext context) { Widget _buildSubjectEmail(BuildContext context) {
return Row( return Focus(
children: [ onFocusChange: (focus) {
Padding( if(focus) {
padding: const EdgeInsets.only(right: 8), controller.htmlEditorApi?.unfocus();
child: Text( controller.keyboardRichTextController.hideRichTextView();
'${AppLocalizations.of(context).subject_email}:', }
style: const TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))), },
Expanded( child: Row(
child: FocusScope(child: Focus( children: [
onFocusChange: (focus) => controller.onSubjectEmailFocusChange(focus), Padding(
child: (TextFieldBuilder() padding: const EdgeInsets.only(right: 8),
..key(const Key('subject_email_input')) child: Text(
..cursorColor(AppColor.colorTextButton) '${AppLocalizations.of(context).subject_email}:',
..maxLines(responsiveUtils.isMobile(context) ? null : 1) style: const TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))),
..onChange((value) => controller.setSubjectEmail(value)) Expanded(
..textStyle(const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal)) child: FocusScope(child: Focus(
..textDecoration(const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none)) onFocusChange: (focus) => controller.onSubjectEmailFocusChange(focus),
..addController(controller.subjectEmailInputController)) child: (TextFieldBuilder()
.build(), ..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(),
))
)
]
),
); );
} }
@@ -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/identities/identity.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/model.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/base/base_controller.dart';
import 'package:tmail_ui_user/features/identity_creator/presentation/model/signature_type.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'; 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<IdentitiesController>(); final _identitiesController = Get.find<IdentitiesController>();
final _accountDashBoardController = Get.find<ManageAccountDashBoardController>(); final _accountDashBoardController = Get.find<ManageAccountDashBoardController>();
final RichTextController keyboardRichTextController = RichTextController();
final HtmlEditorController signatureHtmlEditorController = HtmlEditorController(processNewLineAsBr: true); final HtmlEditorController signatureHtmlEditorController = HtmlEditorController(processNewLineAsBr: true);
final TextEditingController signaturePlainEditorController = TextEditingController(); final TextEditingController signaturePlainEditorController = TextEditingController();
final TextEditingController inputNameIdentityController = TextEditingController(); final TextEditingController inputNameIdentityController = TextEditingController();
@@ -102,6 +105,7 @@ class IdentityCreatorController extends BaseController {
inputNameIdentityController.dispose(); inputNameIdentityController.dispose();
inputBccIdentityController.dispose(); inputBccIdentityController.dispose();
identityCreatorIsActiveWorker.dispose(); identityCreatorIsActiveWorker.dispose();
keyboardRichTextController.dispose();
super.onClose(); super.onClose();
} }
@@ -5,6 +5,8 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:html_editor_enhanced/html_editor.dart' as html_editor_browser; 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: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/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/model/signature_type.dart';
import 'package:tmail_ui_user/features/identity_creator/presentation/widgets/identity_drop_list_field_builder.dart'; import 'package:tmail_ui_user/features/identity_creator/presentation/widgets/identity_drop_list_field_builder.dart';
@@ -279,28 +281,29 @@ class IdentityCreatorView extends GetWidget<IdentityCreatorController> {
: AppLocalizations.of(context).edit_identity.inCaps, : AppLocalizations.of(context).edit_identity.inCaps,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 21, color: Colors.black))), style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 21, color: Colors.black))),
const SizedBox(height: 8), const SizedBox(height: 8),
Expanded(child: SingleChildScrollView( Expanded(child: KeyboardRichText(
physics: const ClampingScrollPhysics(), child: SingleChildScrollView(
reverse: true, physics: const ClampingScrollPhysics(),
child: Padding( reverse: true,
child: Padding(
padding: const EdgeInsets.all(24.0), padding: const EdgeInsets.all(24.0),
child: Column(children: [ child: Column(children: [
Obx(() => (IdentityInputFieldBuilder( Obx(() => (IdentityInputFieldBuilder(
AppLocalizations.of(context).name, AppLocalizations.of(context).name,
controller.errorNameIdentity.value, controller.errorNameIdentity.value,
editingController: controller.inputNameIdentityController, editingController: controller.inputNameIdentityController,
focusNode: controller.inputNameIdentityFocusNode, focusNode: controller.inputNameIdentityFocusNode,
isMandatory: true) isMandatory: true)
..addOnChangeInputNameAction((value) => controller.updateNameIdentity(context, value))) ..addOnChangeInputNameAction((value) => controller.updateNameIdentity(context, value)))
.build()), .build()),
const SizedBox(height: 24), const SizedBox(height: 24),
Obx(() { Obx(() {
if (controller.actionType == IdentityActionType.create) { if (controller.actionType == IdentityActionType.create) {
return (IdentityDropListFieldBuilder( return (IdentityDropListFieldBuilder(
_imagePaths, _imagePaths,
AppLocalizations.of(context).email.inCaps, AppLocalizations.of(context).email.inCaps,
controller.emailOfIdentity.value, controller.emailOfIdentity.value,
controller.listEmailAddressDefault) controller.listEmailAddressDefault)
..addOnSelectEmailAddressDropListAction((emailAddress) => ..addOnSelectEmailAddressDropListAction((emailAddress) =>
controller.updateEmailOfIdentity(emailAddress)) controller.updateEmailOfIdentity(emailAddress))
).build(); ).build();
@@ -313,40 +316,42 @@ class IdentityCreatorView extends GetWidget<IdentityCreatorController> {
}), }),
const SizedBox(height: 24), const SizedBox(height: 24),
Obx(() => (IdentityDropListFieldBuilder( Obx(() => (IdentityDropListFieldBuilder(
_imagePaths, _imagePaths,
AppLocalizations.of(context).reply_to_address, AppLocalizations.of(context).reply_to_address,
controller.replyToOfIdentity.value, controller.replyToOfIdentity.value,
controller.listEmailAddressOfReplyTo) controller.listEmailAddressOfReplyTo)
..addOnSelectEmailAddressDropListAction((newEmailAddress) => ..addOnSelectEmailAddressDropListAction((newEmailAddress) =>
controller.updaterReplyToOfIdentity(newEmailAddress))) controller.updaterReplyToOfIdentity(newEmailAddress)))
.build()), .build()),
const SizedBox(height: 24), const SizedBox(height: 24),
Obx(() => (IdentityInputWithDropListFieldBuilder( Obx(() => (IdentityInputWithDropListFieldBuilder(
AppLocalizations.of(context).bcc_to_address, AppLocalizations.of(context).bcc_to_address,
controller.errorBccIdentity.value, controller.errorBccIdentity.value,
controller.inputBccIdentityController) controller.inputBccIdentityController)
..addOnSelectedSuggestionAction((newEmailAddress) { ..addOnSelectedSuggestionAction((newEmailAddress) {
controller.inputBccIdentityController.text = newEmailAddress?.email ?? ''; controller.inputBccIdentityController.text = newEmailAddress?.email ?? '';
controller.updateBccOfIdentity(newEmailAddress); controller.updateBccOfIdentity(newEmailAddress);
}) })
..addOnChangeInputSuggestionAction((pattern) { ..addOnChangeInputSuggestionAction((pattern) {
controller.validateInputBccAddress(context, pattern); controller.validateInputBccAddress(context, pattern);
if (pattern == null || pattern.trim().isEmpty) { if (pattern == null || pattern.trim().isEmpty) {
controller.updateBccOfIdentity(null); controller.updateBccOfIdentity(null);
} else { } else {
controller.updateBccOfIdentity(EmailAddress(null, pattern)); controller.updateBccOfIdentity(EmailAddress(null, pattern));
} }
}) })
..addOnSuggestionCallbackAction((pattern) => ..addOnSuggestionCallbackAction((pattern) =>
controller.getSuggestionEmailAddress(pattern))) controller.getSuggestionEmailAddress(pattern)))
.build() .build()
), ),
const SizedBox(height: 32), const SizedBox(height: 32),
Text(AppLocalizations.of(context).signature, Text(AppLocalizations.of(context).signature,
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
fontSize: 14, fontSize: 14,
color: AppColor.colorContentEmail)), color: AppColor.colorContentEmail,
),
),
const SizedBox(height: 8), const SizedBox(height: 8),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Obx(() => _buildSignatureButton(context, SignatureType.plainText)), Obx(() => _buildSignatureButton(context, SignatureType.plainText)),
@@ -356,17 +361,18 @@ class IdentityCreatorView extends GetWidget<IdentityCreatorController> {
const SizedBox(height: 8), const SizedBox(height: 8),
Obx(() => Container( Obx(() => Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
border: Border.all(color: AppColor.colorInputBorderCreateMailbox), border: Border.all(color: AppColor.colorInputBorderCreateMailbox),
color: Colors.white), color: Colors.white,
),
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16),
child: Stack( child: Stack(
children: [ children: [
if (controller.signatureType.value == SignatureType.plainText) if (controller.signatureType.value == SignatureType.plainText)
_buildSignaturePlainTextTemplate(context) _buildSignaturePlainTextTemplate(context)
else else
_buildSignatureHtmlTemplate(context) _buildSignatureHtmlTemplate(context)
] ]
), ),
)), )),
const SizedBox(height: 24), const SizedBox(height: 24),
@@ -374,35 +380,48 @@ class IdentityCreatorView extends GetWidget<IdentityCreatorController> {
alignment: Alignment.center, alignment: Alignment.center,
color: Colors.white, color: Colors.white,
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
child: buildTextButton( child: buildTextButton(
AppLocalizations.of(context).cancel, AppLocalizations.of(context).cancel,
textStyle: const TextStyle( textStyle: const TextStyle(
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
fontSize: 17, fontSize: 17,
color: AppColor.colorTextButton), color: AppColor.colorTextButton,
backgroundColor: AppColor.emailAddressChipColor, ),
width: 128, backgroundColor: AppColor.emailAddressChipColor,
height: 44, width: 128,
radius: 10, height: 44,
onTap: () => controller.closeView(context)), radius: 10,
onTap: () => controller.closeView(context),
), ),
const SizedBox(width: 12), ),
Expanded( const SizedBox(width: 12),
child: Obx(() => buildTextButton( Expanded(
controller.actionType == IdentityActionType.create child: Obx(() => buildTextButton(
controller.actionType == IdentityActionType.create
? AppLocalizations.of(context).create ? AppLocalizations.of(context).create
: AppLocalizations.of(context).save, : AppLocalizations.of(context).save,
width: 128, width: 128,
height: 44, height: 44,
radius: 10, radius: 10,
onTap: () => controller.createNewIdentity(context))), 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<IdentityCreatorController> {
return html_editor_mobile.HtmlEditor( return html_editor_mobile.HtmlEditor(
key: const Key('signature_html_editor_mobile'), key: const Key('signature_html_editor_mobile'),
minHeight: 230, minHeight: 230,
onCreated: (htmlEditorController) => controller.signatureHtmlEditorMobileController = htmlEditorController, onCreated: (htmlEditorController) {
controller.keyboardRichTextController.onCreateHTMLEditor(htmlEditorController);
controller.signatureHtmlEditorMobileController = htmlEditorController;
},
initialContent: controller.contentHtmlEditor ?? '', initialContent: controller.contentHtmlEditor ?? '',
); );
} }
+25 -1
View File
@@ -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": "Initializing data...",
"@initializing_data": { "@initializing_data": {
"type": "text", "type": "text",
@@ -2221,5 +2221,29 @@
"type": "text", "type": "text",
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "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": {}
} }
} }
+1 -6
View File
@@ -45,7 +45,7 @@ dependencies:
rich_text_composer: rich_text_composer:
git: git:
url: https://github.com/linagora/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. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
@@ -143,11 +143,6 @@ dependencies:
url: https://github.com/linagora/html-editor-enhanced.git url: https://github.com/linagora/html-editor-enhanced.git
ref: email_supported ref: email_supported
rich_text_composer:
git:
url: https://github.com/linagora/rich-text-composer.git
ref: master
# fk_user_agent # fk_user_agent
fk_user_agent: 2.1.0 fk_user_agent: 2.1.0