TF-912: Support rich text for signature htmlBody of identity
This commit is contained in:
@@ -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 ?? '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user