TF-105 Change composer library html-editor-enhanced to enough_html_editor support display email content better
This commit is contained in:
@@ -26,4 +26,8 @@ extension HtmlExtension on String {
|
|||||||
}
|
}
|
||||||
return htmlString;
|
return htmlString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String addBlockQuoteTag() => addBlockTag(
|
||||||
|
'blockquote',
|
||||||
|
attribute: 'style=\"margin-left:8px;margin-right:8px;padding-left:12px;padding-right:12px;border-left:5px solid #eee;\"');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
String? _htmlData;
|
String? _htmlData;
|
||||||
bool? _wereExternalImagesBlocked;
|
bool? _wereExternalImagesBlocked;
|
||||||
bool _isGenerating = false;
|
bool _isGenerating = false;
|
||||||
double? _webViewContentHeight;
|
double? _webViewContentHeight = 1.0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -114,7 +114,7 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
|
|
||||||
final size = MediaQuery.of(context).size;
|
final size = MediaQuery.of(context).size;
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: _webViewContentHeight ?? size.height,
|
height: _webViewContentHeight,
|
||||||
width: size.width,
|
width: size.width,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(top: 16),
|
padding: EdgeInsets.only(top: 16),
|
||||||
|
|||||||
+4
-5
@@ -59,12 +59,11 @@ dependencies:
|
|||||||
# getwidget
|
# getwidget
|
||||||
getwidget: 2.0.4
|
getwidget: 2.0.4
|
||||||
|
|
||||||
# Used to fix bugs in the official version of flutter_inappwebview: https://githubmemory.com/repo/pichillilorenzo/flutter_inappwebview/issues/873
|
# enough_html_editor
|
||||||
# flutter_inappwebview
|
enough_html_editor:
|
||||||
flutter_inappwebview:
|
|
||||||
git:
|
git:
|
||||||
url: git://github.com/robert-virkus/flutter_inappwebview.git
|
url: git://github.com/dab246/enough_html_editor.git
|
||||||
ref: master
|
ref: fix_bug_enough_platform_widgets
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:core/core.dart';
|
|||||||
import 'package:core/presentation/utils/app_toast.dart';
|
import 'package:core/presentation/utils/app_toast.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:html_editor_enhanced/html_editor.dart';
|
|
||||||
import 'package:tmail_ui_user/features/composer/data/datasource/autocomplete_datasource.dart';
|
import 'package:tmail_ui_user/features/composer/data/datasource/autocomplete_datasource.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/data/datasource/composer_datasource.dart';
|
import 'package:tmail_ui_user/features/composer/data/datasource/composer_datasource.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/data/datasource/contact_datasource.dart';
|
import 'package:tmail_ui_user/features/composer/data/datasource/contact_datasource.dart';
|
||||||
@@ -53,7 +52,6 @@ class ComposerBindings extends Bindings {
|
|||||||
Get.lazyPut<AutoCompleteRepository>(() => Get.find<AutoCompleteRepositoryImpl>());
|
Get.lazyPut<AutoCompleteRepository>(() => Get.find<AutoCompleteRepositoryImpl>());
|
||||||
Get.lazyPut(() => GetAutoCompleteInteractor(Get.find<AutoCompleteRepository>()));
|
Get.lazyPut(() => GetAutoCompleteInteractor(Get.find<AutoCompleteRepository>()));
|
||||||
Get.lazyPut(() => Uuid());
|
Get.lazyPut(() => Uuid());
|
||||||
Get.lazyPut(() => HtmlEditorController());
|
|
||||||
Get.lazyPut(() => TextEditingController());
|
Get.lazyPut(() => TextEditingController());
|
||||||
Get.lazyPut(() => ContactDataSourceImpl());
|
Get.lazyPut(() => ContactDataSourceImpl());
|
||||||
Get.lazyPut<ContactDataSource>(() => Get.find<ContactDataSourceImpl>());
|
Get.lazyPut<ContactDataSource>(() => Get.find<ContactDataSourceImpl>());
|
||||||
@@ -74,7 +72,6 @@ class ComposerBindings extends Bindings {
|
|||||||
Get.find<GetAutoCompleteWithDeviceContactInteractor>(),
|
Get.find<GetAutoCompleteWithDeviceContactInteractor>(),
|
||||||
Get.find<AppToast>(),
|
Get.find<AppToast>(),
|
||||||
Get.find<Uuid>(),
|
Get.find<Uuid>(),
|
||||||
Get.find<HtmlEditorController>(),
|
|
||||||
Get.find<TextEditingController>(),
|
Get.find<TextEditingController>(),
|
||||||
Get.find<HtmlMessagePurifier>(),
|
Get.find<HtmlMessagePurifier>(),
|
||||||
Get.find<LocalFilePickerInteractor>(),
|
Get.find<LocalFilePickerInteractor>(),
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import 'dart:async';
|
|||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
import 'package:core/presentation/utils/app_toast.dart';
|
import 'package:core/presentation/utils/app_toast.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:enough_html_editor/enough_html_editor.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:html_editor_enhanced/html_editor.dart';
|
|
||||||
import 'package:http_parser/http_parser.dart';
|
import 'package:http_parser/http_parser.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||||
@@ -55,7 +55,6 @@ class ComposerController extends BaseController {
|
|||||||
final GetAutoCompleteWithDeviceContactInteractor _getAutoCompleteWithDeviceContactInteractor;
|
final GetAutoCompleteWithDeviceContactInteractor _getAutoCompleteWithDeviceContactInteractor;
|
||||||
final AppToast _appToast;
|
final AppToast _appToast;
|
||||||
final Uuid _uuid;
|
final Uuid _uuid;
|
||||||
final HtmlEditorController composerEditorController;
|
|
||||||
final TextEditingController subjectEmailInputController;
|
final TextEditingController subjectEmailInputController;
|
||||||
final HtmlMessagePurifier _htmlMessagePurifier;
|
final HtmlMessagePurifier _htmlMessagePurifier;
|
||||||
final LocalFilePickerInteractor _localFilePickerInteractor;
|
final LocalFilePickerInteractor _localFilePickerInteractor;
|
||||||
@@ -66,6 +65,7 @@ class ComposerController extends BaseController {
|
|||||||
List<EmailAddress> listBccEmailAddress = [];
|
List<EmailAddress> listBccEmailAddress = [];
|
||||||
String? _subjectEmail;
|
String? _subjectEmail;
|
||||||
ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.localContact;
|
ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.localContact;
|
||||||
|
HtmlEditorApi? htmlEditorApi;
|
||||||
|
|
||||||
void setSubjectEmail(String subject) => _subjectEmail = subject;
|
void setSubjectEmail(String subject) => _subjectEmail = subject;
|
||||||
|
|
||||||
@@ -76,7 +76,6 @@ class ComposerController extends BaseController {
|
|||||||
this._getAutoCompleteWithDeviceContactInteractor,
|
this._getAutoCompleteWithDeviceContactInteractor,
|
||||||
this._appToast,
|
this._appToast,
|
||||||
this._uuid,
|
this._uuid,
|
||||||
this.composerEditorController,
|
|
||||||
this.subjectEmailInputController,
|
this.subjectEmailInputController,
|
||||||
this._htmlMessagePurifier,
|
this._htmlMessagePurifier,
|
||||||
this._localFilePickerInteractor,
|
this._localFilePickerInteractor,
|
||||||
@@ -139,13 +138,14 @@ class ComposerController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void initContentEmail() {
|
String getContentEmail() {
|
||||||
if (composerArguments.value != null
|
if (composerArguments.value != null
|
||||||
&& composerArguments.value!.emailActionType != EmailActionType.compose
|
&& composerArguments.value!.emailActionType != EmailActionType.compose
|
||||||
&& Get.context != null) {
|
&& Get.context != null) {
|
||||||
final contentEmailQuoted = _getBodyEmailQuotedAsHtml(Get.context!, _htmlMessagePurifier);
|
final contentEmailQuoted = _getBodyEmailQuotedAsHtml(Get.context!, _htmlMessagePurifier);
|
||||||
composerEditorController.setText(contentEmailQuoted);
|
return contentEmailQuoted;
|
||||||
}
|
}
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple2<String, String>? getHeaderEmailQuoted(String locale) {
|
Tuple2<String, String>? getHeaderEmailQuoted(String locale) {
|
||||||
@@ -238,24 +238,11 @@ class ComposerController extends BaseController {
|
|||||||
|
|
||||||
var trustAsHtml = '';
|
var trustAsHtml = '';
|
||||||
if (contentEmail != null && contentEmail.value1.isNotEmpty) {
|
if (contentEmail != null && contentEmail.value1.isNotEmpty) {
|
||||||
|
|
||||||
final messageContent = contentEmail.value1.first;
|
final messageContent = contentEmail.value1.first;
|
||||||
final attachmentInlines = contentEmail.value2;
|
trustAsHtml = messageContent.content;
|
||||||
final session = contentEmail.value3;
|
|
||||||
final baseDownloadUrl = session.getDownloadUrl();
|
|
||||||
final accountId = session.accounts.keys.first;
|
|
||||||
|
|
||||||
final message = (attachmentInlines.isNotEmpty && messageContent.hasImageInlineWithCid())
|
|
||||||
? '${messageContent.getContentHasInlineAttachment(baseDownloadUrl, accountId, attachmentInlines)}'
|
|
||||||
: '${messageContent.content}';
|
|
||||||
|
|
||||||
trustAsHtml = htmlMessagePurifier.purifyHtmlMessage(message, allowAttributes: {'style', 'input', 'form'})
|
|
||||||
.addBlockTag('div', attribute: 'style=\"margin-left:8px;margin-right:8px;padding-left:12px;padding-right:12px;border-left:6px solid #EFEFEF;\"')
|
|
||||||
.addNewLineTag(count: 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final emailQuotedHtml = '$headerEmailQuotedAsHtml$trustAsHtml'
|
final emailQuotedHtml = '</br></br></br>$headerEmailQuotedAsHtml${trustAsHtml.addBlockQuoteTag()}</br>';
|
||||||
.addBlockTag('div', attribute: 'style=\"padding-right:16px;padding-left:16px;background-color:#FBFBFF;width:100%\"');
|
|
||||||
|
|
||||||
return emailQuotedHtml;
|
return emailQuotedHtml;
|
||||||
}
|
}
|
||||||
@@ -269,7 +256,7 @@ class ComposerController extends BaseController {
|
|||||||
final generatePartId = PartId(_uuid.v1());
|
final generatePartId = PartId(_uuid.v1());
|
||||||
final generateBlobId = Id(_uuid.v1());
|
final generateBlobId = Id(_uuid.v1());
|
||||||
|
|
||||||
final emailBodyText = await composerEditorController.getText();
|
final emailBodyText = (await htmlEditorApi?.getFullHtml()) ?? '';
|
||||||
|
|
||||||
return Email(
|
return Email(
|
||||||
generateEmailId,
|
generateEmailId,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
|
import 'package:enough_html_editor/enough_html_editor.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
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';
|
import 'package:model/model.dart';
|
||||||
import 'package:model/email/attachment.dart';
|
|
||||||
import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.dart';
|
import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_composer_widget_builder.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_composer_widget_builder.dart';
|
||||||
@@ -22,7 +22,10 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
|
onTap: () {
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus();
|
||||||
|
controller.htmlEditorApi?.unfocus();
|
||||||
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
backgroundColor: AppColor.primaryLightColor,
|
backgroundColor: AppColor.primaryLightColor,
|
||||||
@@ -148,34 +151,46 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_buildEmailHeader(context),
|
_buildEmailHeader(context),
|
||||||
Padding(
|
Container(
|
||||||
padding: EdgeInsets.only(bottom: 30, top: 16, left: 16, right: 16),
|
color: AppColor.primaryLightColor,
|
||||||
child: _buildComposerEditor(context),
|
margin: EdgeInsets.zero,
|
||||||
),
|
padding: EdgeInsets.zero,
|
||||||
_buildAttachmentsLoadingView(),
|
alignment: Alignment.topCenter,
|
||||||
_buildAttachments(context),
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 8, left: 16, right: 16, bottom: 30),
|
||||||
|
child: _buildComposerEditor(context)),
|
||||||
|
_buildAttachmentsLoadingView(),
|
||||||
|
_buildAttachments(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildComposerEditor(BuildContext context) {
|
Widget _buildComposerEditor(BuildContext context) {
|
||||||
return HtmlEditor(
|
return Obx(() {
|
||||||
key: Key('email_body_editor_quoted'),
|
if (controller.composerArguments.value?.emailActionType == EmailActionType.compose) {
|
||||||
controller: controller.composerEditorController,
|
return PackagedHtmlEditor(
|
||||||
htmlEditorOptions: HtmlEditorOptions(
|
key: Key('composer_editor'),
|
||||||
hint: AppLocalizations.of(context).hint_content_email_composer,
|
minHeight: 400,
|
||||||
shouldEnsureVisible: true),
|
onCreated: (editorApi) => controller.htmlEditorApi = editorApi,
|
||||||
otherOptions: OtherOptions(decoration: BoxDecoration()),
|
);
|
||||||
callbacks: Callbacks(
|
} else {
|
||||||
onFocus: () {
|
final message = controller.getContentEmail();
|
||||||
FocusManager.instance.primaryFocus?.unfocus();
|
return message.isNotEmpty
|
||||||
},
|
? PackagedHtmlEditor(
|
||||||
onInit: () {
|
key: Key('composer_editor'),
|
||||||
controller.initContentEmail();
|
minHeight: 400,
|
||||||
}
|
onCreated: (editorApi) => controller.htmlEditorApi = editorApi,
|
||||||
),
|
initialContent: message,
|
||||||
);
|
)
|
||||||
|
: SizedBox.shrink();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAttachmentsLoadingView() {
|
Widget _buildAttachmentsLoadingView() {
|
||||||
|
|||||||
+6
-6
@@ -86,12 +86,6 @@ dependencies:
|
|||||||
url: git://github.com/dab246/flutter_chips_input.git
|
url: git://github.com/dab246/flutter_chips_input.git
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
# html_editor_enhanced
|
|
||||||
html_editor_enhanced:
|
|
||||||
git:
|
|
||||||
url: git://github.com/dab246/html-editor-enhanced.git
|
|
||||||
ref: master
|
|
||||||
|
|
||||||
# uuid
|
# uuid
|
||||||
uuid: 3.0.4
|
uuid: 3.0.4
|
||||||
|
|
||||||
@@ -125,6 +119,12 @@ dependencies:
|
|||||||
# hive
|
# hive
|
||||||
hive: 2.0.4
|
hive: 2.0.4
|
||||||
|
|
||||||
|
# enough_html_editor
|
||||||
|
enough_html_editor:
|
||||||
|
git:
|
||||||
|
url: git://github.com/dab246/enough_html_editor.git
|
||||||
|
ref: fix_bug_enough_platform_widgets
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|||||||
Reference in New Issue
Block a user