From 0cc2a2f0cce872b18ddd3ca92447f402217ad1b9 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 13 Jun 2023 17:26:50 +0700 Subject: [PATCH] TF-1885 Support RTL mode for HTML composer (cherry picked from commit 8a4569ef3a749e0f3abc0a385aa915d749b16a9a) --- .../utils/html_transformer/html_template.dart | 28 +------ .../utils/html_transformer/html_utils.dart | 45 +++++++++++ .../composer/presentation/composer_view.dart | 2 + .../presentation/composer_view_web.dart | 48 ++++-------- .../widgets/email_editor_widget.dart | 75 +++++++++++++++++++ .../presentation/identity_creator_view.dart | 8 +- .../presentation/vacation/vacation_view.dart | 12 ++- pubspec.lock | 4 +- pubspec.yaml | 5 ++ 9 files changed, 156 insertions(+), 71 deletions(-) create mode 100644 lib/features/composer/presentation/widgets/email_editor_widget.dart diff --git a/core/lib/presentation/utils/html_transformer/html_template.dart b/core/lib/presentation/utils/html_transformer/html_template.dart index ebeeea56e..2e8a875a8 100644 --- a/core/lib/presentation/utils/html_transformer/html_template.dart +++ b/core/lib/presentation/utils/html_transformer/html_template.dart @@ -61,30 +61,4 @@ String generateHtml(String content, { '''; -} - -const bodyCssStyleForEditor = ''' - -'''; \ No newline at end of file +} \ No newline at end of file diff --git a/core/lib/presentation/utils/html_transformer/html_utils.dart b/core/lib/presentation/utils/html_transformer/html_utils.dart index cca0948f2..c148b3bd2 100644 --- a/core/lib/presentation/utils/html_transformer/html_utils.dart +++ b/core/lib/presentation/utils/html_transformer/html_utils.dart @@ -1,5 +1,7 @@ import 'package:core/presentation/utils/html_transformer/html_event_action.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:flutter/material.dart'; class HtmlUtils { @@ -65,4 +67,47 @@ class HtmlUtils { yDown = null; } '''; + + static String customCssStyleHtmlEditor({TextDirection direction = TextDirection.ltr}) { + if (PlatformInfo.isWeb) { + return ''' + + '''; + } else if (PlatformInfo.isMobile) { + return ''' + #editor { + direction: ${direction.name}; + } + '''; + } else { + return ''; + } + } } diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index f5afe1811..2f9dea9a7 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -3,6 +3,7 @@ import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/html_extension.dart'; import 'package:core/presentation/extensions/string_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/html_transformer/html_utils.dart'; import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/views/button/icon_button_web.dart'; @@ -690,6 +691,7 @@ class ComposerView extends GetWidget minHeight: 550, addDefaultSelectionMenuItems: false, initialContent: initialContent ?? '', + customStyleCss: HtmlUtils.customCssStyleHtmlEditor(direction: AppUtils.getCurrentDirection(context)), onCreated: (editorApi) => controller.initRichTextForMobile(context, editorApi, initialContent) ), ), diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index bd2cada39..5459b61b4 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -4,7 +4,6 @@ import 'package:core/presentation/extensions/html_extension.dart'; import 'package:core/presentation/extensions/string_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/utils/app_toast.dart'; -import 'package:core/presentation/utils/html_transformer/html_template.dart'; import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/views/button/icon_button_web.dart'; @@ -18,7 +17,6 @@ import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:html_editor_enhanced/html_editor.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:model/email/email_action_type.dart'; import 'package:model/email/prefix_email_address.dart'; @@ -32,6 +30,7 @@ import 'package:tmail_ui_user/features/composer/presentation/mixin/rich_text_but import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/email_editor_widget.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/toolbar_rich_text_builder.dart'; import 'package:tmail_ui_user/features/upload/presentation/extensions/list_upload_file_state_extension.dart'; import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart'; @@ -80,7 +79,10 @@ class ComposerView extends GetWidget _buildAttachmentsWidget(context), ToolbarRichTextWebBuilder(richTextWebController: controller.richTextWebController), buildInlineLoadingView(controller), - _buildEditorForm(context) + Expanded(child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: _buildEditorForm(context) + )) ] )), ] @@ -442,7 +444,10 @@ class ComposerView extends GetWidget _buildAttachmentsWidget(context), ToolbarRichTextWebBuilder(richTextWebController: controller.richTextWebController), buildInlineLoadingView(controller), - _buildEditorForm(context) + Expanded(child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: _buildEditorForm(context) + )) ] ))), const Divider(color: AppColor.colorDividerComposer, height: 1), @@ -815,37 +820,10 @@ class ComposerView extends GetWidget } Widget _buildHtmlEditor(BuildContext context, String initContent) { - log('ComposerView::_buildHtmlEditor(): initContent: $initContent'); - return Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: HtmlEditor( - key: const Key('composer_editor_web'), - controller: controller.richTextWebController.editorController, - htmlEditorOptions: const HtmlEditorOptions( - shouldEnsureVisible: true, - hint: '', - darkMode: false, - customBodyCssStyle: bodyCssStyleForEditor - ), - blockQuotedContent: initContent, - htmlToolbarOptions: const HtmlToolbarOptions( - toolbarType: ToolbarType.hide, - defaultToolbarButtons: [] - ), - otherOptions: const OtherOptions(height: 550), - callbacks: Callbacks( - onBeforeCommand: controller.onChangeTextEditorWeb, - onChangeContent: controller.onChangeTextEditorWeb, - onInit: () => controller.handleInitHtmlEditorWeb(initContent), - onFocus: controller.handleOnFocusHtmlEditorWeb, - onBlur: controller.handleOnUnFocusHtmlEditorWeb, - onMouseDown: () => controller.handleOnMouseDownHtmlEditorWeb(context), - onChangeSelection: controller.richTextWebController.onEditorSettingsChange, - onChangeCodeview: controller.onChangeTextEditorWeb - ), - ) - ) + return EmailEditorWidget( + controller: controller, + content: initContent, + direction: AppUtils.getCurrentDirection(context), ); } diff --git a/lib/features/composer/presentation/widgets/email_editor_widget.dart b/lib/features/composer/presentation/widgets/email_editor_widget.dart new file mode 100644 index 000000000..ee4eb1666 --- /dev/null +++ b/lib/features/composer/presentation/widgets/email_editor_widget.dart @@ -0,0 +1,75 @@ + +import 'package:core/presentation/utils/html_transformer/html_utils.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:flutter/material.dart'; +import 'package:html_editor_enhanced/html_editor.dart'; +import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; + +class EmailEditorWidget extends StatefulWidget { + + final ComposerController controller; + final String content; + final TextDirection direction; + + const EmailEditorWidget({ + super.key, + required this.controller, + required this.content, + required this.direction, + }); + + @override + State createState() => _EmailEditorState(); +} + +class _EmailEditorState extends State { + + late ComposerController _controller; + late HtmlEditorController _editorController; + + @override + void initState() { + _controller = widget.controller; + _editorController = _controller.richTextWebController.editorController; + super.initState(); + } + + @override + void didUpdateWidget(covariant EmailEditorWidget oldWidget) { + log('_EmailEditorState::didUpdateWidget():Old: ${oldWidget.direction} | current: ${widget.direction}'); + if (oldWidget.direction != widget.direction) { + _editorController.updateBodyDirection(widget.direction.name); + } + super.didUpdateWidget(oldWidget); + } + + @override + Widget build(BuildContext context) { + return HtmlEditor( + key: const Key('composer_editor_web'), + controller: _editorController, + htmlEditorOptions: HtmlEditorOptions( + shouldEnsureVisible: true, + hint: '', + darkMode: false, + customBodyCssStyle: HtmlUtils.customCssStyleHtmlEditor(direction: widget.direction), + ), + blockQuotedContent: widget.content, + htmlToolbarOptions: const HtmlToolbarOptions( + toolbarType: ToolbarType.hide, + defaultToolbarButtons: [] + ), + otherOptions: const OtherOptions(height: 550), + callbacks: Callbacks( + onBeforeCommand: _controller.onChangeTextEditorWeb, + onChangeContent: _controller.onChangeTextEditorWeb, + onInit: () => _controller.handleInitHtmlEditorWeb(widget.content), + onFocus: _controller.handleOnFocusHtmlEditorWeb, + onBlur: _controller.handleOnUnFocusHtmlEditorWeb, + onMouseDown: () => _controller.handleOnMouseDownHtmlEditorWeb(context), + onChangeSelection: _controller.richTextWebController.onEditorSettingsChange, + onChangeCodeview: _controller.onChangeTextEditorWeb + ), + ); + } +} \ 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 180fe7ab2..93be687b1 100644 --- a/lib/features/identity_creator/presentation/identity_creator_view.dart +++ b/lib/features/identity_creator/presentation/identity_creator_view.dart @@ -3,7 +3,7 @@ import 'dart:math' as math; import 'package:core/presentation/extensions/capitalize_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/utils/html_transformer/html_template.dart'; +import 'package:core/presentation/utils/html_transformer/html_utils.dart'; import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/views/button/icon_button_web.dart'; @@ -365,10 +365,11 @@ class IdentityCreatorView extends GetWidget { child: html_editor_browser.HtmlEditor( key: const Key('identity_create_editor_web'), controller: controller.richTextWebController.editorController, - htmlEditorOptions: const html_editor_browser.HtmlEditorOptions( + htmlEditorOptions: html_editor_browser.HtmlEditorOptions( hint: '', darkMode: false, - customBodyCssStyle: bodyCssStyleForEditor), + customBodyCssStyle: HtmlUtils.customCssStyleHtmlEditor(direction: AppUtils.getCurrentDirection(context)), + ), blockQuotedContent: initContent, htmlToolbarOptions: const html_editor_browser.HtmlToolbarOptions( toolbarType: html_editor_browser.ToolbarType.hide, @@ -409,6 +410,7 @@ class IdentityCreatorView extends GetWidget { minHeight: controller.htmlEditorMinHeight, addDefaultSelectionMenuItems: false, initialContent: initialContent ?? '', + customStyleCss: HtmlUtils.customCssStyleHtmlEditor(direction: AppUtils.getCurrentDirection(context)), onCreated: (editorApi) => controller.initRichTextForMobile(context, editorApi), ), ); diff --git a/lib/features/manage_account/presentation/vacation/vacation_view.dart b/lib/features/manage_account/presentation/vacation/vacation_view.dart index ac3d9e9ac..202befe00 100644 --- a/lib/features/manage_account/presentation/vacation/vacation_view.dart +++ b/lib/features/manage_account/presentation/vacation/vacation_view.dart @@ -1,5 +1,6 @@ import 'package:core/core.dart'; +import 'package:core/presentation/utils/html_transformer/html_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; @@ -19,6 +20,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/vacation/util import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_controller.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:html_editor_enhanced/html_editor.dart' as html_editor_browser; +import 'package:tmail_ui_user/main/utils/app_utils.dart'; class VacationView extends GetWidget with RichTextButtonMixin { @@ -430,10 +432,11 @@ class VacationView extends GetWidget with RichTextButtonMixi return html_editor_browser.HtmlEditor( key: const Key('vacation_message_html_text_editor_web'), controller: controller.richTextControllerForWeb.editorController, - htmlEditorOptions: const html_editor_browser.HtmlEditorOptions( - hint: '', - darkMode: false, - customBodyCssStyle: bodyCssStyleForEditor), + htmlEditorOptions: html_editor_browser.HtmlEditorOptions( + hint: '', + darkMode: false, + customBodyCssStyle: HtmlUtils.customCssStyleHtmlEditor(direction: AppUtils.getCurrentDirection(context)) + ), blockQuotedContent: controller.vacationMessageHtmlText ?? '', htmlToolbarOptions: const html_editor_browser.HtmlToolbarOptions( toolbarType: html_editor_browser.ToolbarType.hide, @@ -459,6 +462,7 @@ class VacationView extends GetWidget with RichTextButtonMixi minHeight: controller.htmlEditorMinHeight, addDefaultSelectionMenuItems: false, initialContent: controller.vacationMessageHtmlText ?? '', + customStyleCss: HtmlUtils.customCssStyleHtmlEditor(direction: AppUtils.getCurrentDirection(context)), onCreated: (htmlApi) { controller.richTextControllerForMobile.onCreateHTMLEditor( htmlApi, diff --git a/pubspec.lock b/pubspec.lock index 009307386..74c3420da 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -865,8 +865,8 @@ packages: dependency: "direct main" description: path: "." - ref: email_supported - resolved-ref: e056608ac04b471ed2da801794cbae7ee50cf643 + ref: support_rtl + resolved-ref: "4f4896ce1dc23011ad0b0c8d94a680c3fed6b750" url: "https://github.com/linagora/html-editor-enhanced.git" source: git version: "2.5.1" diff --git a/pubspec.yaml b/pubspec.yaml index 3042b6837..33adc13bc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -223,6 +223,11 @@ dependency_overrides: flutter_keyboard_visibility: 5.4.1 + html_editor_enhanced: + git: + url: https://github.com/linagora/html-editor-enhanced.git + ref: support_rtl + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec