TF-2520 Create some class to handle html

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2024-03-06 12:21:46 +07:00
committed by Dat H. Pham
parent ab2c32154e
commit ebd62ab78f
17 changed files with 276 additions and 270 deletions
+95 -104
View File
@@ -1,101 +1,10 @@
library core;
// Extensions
export 'presentation/extensions/color_extension.dart';
export 'presentation/extensions/url_extension.dart';
export 'presentation/extensions/uri_extension.dart';
export 'presentation/extensions/capitalize_extension.dart';
export 'presentation/extensions/list_extensions.dart';
export 'domain/extensions/datetime_extension.dart';
export 'presentation/extensions/html_extension.dart';
export 'presentation/extensions/compare_string_extension.dart';
export 'presentation/extensions/compare_list_extensions.dart';
export 'presentation/extensions/string_extension.dart';
export 'presentation/extensions/tap_down_details_extension.dart';
export 'domain/extensions/media_type_extension.dart';
export 'presentation/extensions/map_extensions.dart';
// Exceptions
export 'domain/exceptions/download_file_exception.dart';
export 'data/extensions/options_extensions.dart';
export 'domain/exceptions/web_session_exception.dart';
// Utils
export 'presentation/utils/theme_utils.dart';
export 'presentation/utils/responsive_utils.dart';
export 'presentation/utils/keyboard_utils.dart';
export 'presentation/utils/icon_utils.dart';
export 'presentation/utils/style_utils.dart';
export 'presentation/utils/app_toast.dart';
export 'presentation/utils/html_transformer/html_template.dart';
export 'presentation/utils/html_transformer/html_transform.dart';
export 'presentation/utils/html_transformer/transform_configuration.dart';
export 'presentation/utils/html_transformer/dom/add_tooltip_link_transformers.dart';
export 'data/utils/device_manager.dart';
export 'utils/app_logger.dart';
export 'utils/benchmark.dart';
export 'utils/fps_manager.dart';
export 'utils/build_utils.dart';
export 'utils/string_convert.dart';
export 'utils/config/app_config_loader.dart';
export 'utils/config/app_config_parser.dart';
export 'utils/config/errors.dart';
export 'data/utils/compress_file_utils.dart';
export 'utils/platform_info.dart';
export 'utils/option_param_mixin.dart';
export 'utils/file_utils.dart';
export 'utils/print_utils.dart';
// Views
export 'presentation/views/text/slogan_builder.dart';
export 'presentation/views/text/text_field_builder.dart';
export 'presentation/views/text/text_form_field_builder.dart';
export 'presentation/views/text/input_decoration_builder.dart';
export 'presentation/views/text/rich_text_builder.dart';
export 'presentation/views/text/text_overflow_builder.dart';
export 'presentation/views/responsive/responsive_widget.dart';
export 'presentation/views/list/tree_view.dart';
export 'presentation/views/button/icon_button_web.dart';
export 'presentation/views/button/tmail_button_widget.dart';
export 'presentation/views/image/avatar_builder.dart';
export 'presentation/views/list/sliver_grid_delegate_fixed_height.dart';
export 'presentation/views/image/icon_builder.dart';
export 'presentation/views/context_menu/context_menu_action_builder.dart';
export 'presentation/views/context_menu/context_menu_builder.dart';
export 'presentation/views/context_menu/context_menu_header_builder.dart';
export 'presentation/views/context_menu/simple_context_menu_action_builder.dart';
export 'presentation/views/dialog/downloading_file_dialog_builder.dart';
export 'presentation/views/dialog/confirmation_dialog_builder.dart';
export 'presentation/views/dialog/edit_text_dialog_builder.dart';
export 'presentation/views/dialog/color_picker_dialog_builder.dart';
export 'presentation/views/html_viewer/html_content_viewer_widget.dart';
export 'presentation/views/html_viewer/html_content_viewer_on_web_widget.dart';
export 'presentation/views/floating_button/scrolling_floating_button_animated.dart';
export 'presentation/views/bottom_popup/cupertino_action_sheet_action_builder.dart';
export 'presentation/views/bottom_popup/cupertino_action_sheet_no_icon_builder.dart';
export 'presentation/views/bottom_popup/cupertino_action_sheet_builder.dart';
export 'presentation/views/bottom_popup/confirmation_dialog_action_sheet_builder.dart';
export 'presentation/views/modal_sheets/edit_text_modal_sheet_builder.dart';
export 'presentation/views/search/search_bar_view.dart';
export 'presentation/views/popup_menu/popup_menu_item_widget.dart';
export 'presentation/views/quick_search/quick_search_input_form.dart';
export 'presentation/views/toast/toast_position.dart';
export 'presentation/views/toast/tmail_toast.dart';
export 'presentation/views/bottom_popup/full_screen_action_sheet_builder.dart';
export 'presentation/views/checkbox/labeled_checkbox.dart';
export 'presentation/views/container/tmail_container_widget.dart';
export 'presentation/views/clipper/side_arrow_clipper.dart';
export 'presentation/views/avatar/gradient_circle_avatar_icon.dart';
export 'presentation/views/loading/cupertino_loading_widget.dart';
// Resources
export 'presentation/resources/assets_paths.dart';
export 'presentation/resources/image_paths.dart';
// Constants
export 'presentation/constants/constants_ui.dart';
export 'data/constants/constant.dart';
export 'data/extensions/options_extensions.dart';
export 'data/model/query/query_parameter.dart';
// Model
export 'data/model/source_type/data_source_type.dart';
// Network
export 'data/network/config/dynamic_url_interceptors.dart';
export 'data/network/config/service_path.dart';
@@ -103,14 +12,96 @@ export 'data/network/dio_client.dart';
export 'data/network/download/download_client.dart';
export 'data/network/download/download_manager.dart';
export 'data/network/download/downloaded_response.dart';
export 'data/utils/compress_file_utils.dart';
export 'data/utils/device_manager.dart';
// Exceptions
export 'domain/exceptions/download_file_exception.dart';
export 'domain/exceptions/web_session_exception.dart';
export 'domain/extensions/datetime_extension.dart';
export 'domain/extensions/media_type_extension.dart';
// Action
export 'presentation/action/action_callback_define.dart';
// Constants
export 'presentation/constants/constants_ui.dart';
export 'presentation/extensions/capitalize_extension.dart';
// Extensions
export 'presentation/extensions/color_extension.dart';
export 'presentation/extensions/compare_list_extensions.dart';
export 'presentation/extensions/compare_string_extension.dart';
export 'presentation/extensions/html_extension.dart';
export 'presentation/extensions/list_extensions.dart';
export 'presentation/extensions/map_extensions.dart';
export 'presentation/extensions/string_extension.dart';
export 'presentation/extensions/tap_down_details_extension.dart';
export 'presentation/extensions/uri_extension.dart';
export 'presentation/extensions/url_extension.dart';
// Resources
export 'presentation/resources/assets_paths.dart';
export 'presentation/resources/image_paths.dart';
export 'presentation/state/failure.dart';
// State
export 'presentation/state/success.dart';
export 'presentation/state/failure.dart';
// Model
export 'data/model/source_type/data_source_type.dart';
export 'data/model/query/query_parameter.dart';
// Action
export 'presentation/action/action_callback_define.dart';
export 'presentation/utils/app_toast.dart';
export 'presentation/utils/html_transformer/dom/add_tooltip_link_transformers.dart';
export 'presentation/utils/html_transformer/html_transform.dart';
export 'presentation/utils/html_transformer/transform_configuration.dart';
export 'presentation/utils/icon_utils.dart';
export 'presentation/utils/keyboard_utils.dart';
export 'presentation/utils/responsive_utils.dart';
export 'presentation/utils/style_utils.dart';
// Utils
export 'presentation/utils/theme_utils.dart';
export 'presentation/views/avatar/gradient_circle_avatar_icon.dart';
export 'presentation/views/bottom_popup/confirmation_dialog_action_sheet_builder.dart';
export 'presentation/views/bottom_popup/cupertino_action_sheet_action_builder.dart';
export 'presentation/views/bottom_popup/cupertino_action_sheet_builder.dart';
export 'presentation/views/bottom_popup/cupertino_action_sheet_no_icon_builder.dart';
export 'presentation/views/bottom_popup/full_screen_action_sheet_builder.dart';
export 'presentation/views/button/icon_button_web.dart';
export 'presentation/views/button/tmail_button_widget.dart';
export 'presentation/views/checkbox/labeled_checkbox.dart';
export 'presentation/views/clipper/side_arrow_clipper.dart';
export 'presentation/views/container/tmail_container_widget.dart';
export 'presentation/views/context_menu/context_menu_action_builder.dart';
export 'presentation/views/context_menu/context_menu_builder.dart';
export 'presentation/views/context_menu/context_menu_header_builder.dart';
export 'presentation/views/context_menu/simple_context_menu_action_builder.dart';
export 'presentation/views/dialog/color_picker_dialog_builder.dart';
export 'presentation/views/dialog/confirmation_dialog_builder.dart';
export 'presentation/views/dialog/downloading_file_dialog_builder.dart';
export 'presentation/views/dialog/edit_text_dialog_builder.dart';
export 'presentation/views/floating_button/scrolling_floating_button_animated.dart';
export 'presentation/views/html_viewer/html_content_viewer_on_web_widget.dart';
export 'presentation/views/html_viewer/html_content_viewer_widget.dart';
export 'presentation/views/image/avatar_builder.dart';
export 'presentation/views/image/icon_builder.dart';
export 'presentation/views/list/sliver_grid_delegate_fixed_height.dart';
export 'presentation/views/list/tree_view.dart';
export 'presentation/views/loading/cupertino_loading_widget.dart';
export 'presentation/views/modal_sheets/edit_text_modal_sheet_builder.dart';
export 'presentation/views/popup_menu/popup_menu_item_widget.dart';
export 'presentation/views/quick_search/quick_search_input_form.dart';
export 'presentation/views/responsive/responsive_widget.dart';
export 'presentation/views/search/search_bar_view.dart';
export 'presentation/views/text/input_decoration_builder.dart';
export 'presentation/views/text/rich_text_builder.dart';
// Views
export 'presentation/views/text/slogan_builder.dart';
export 'presentation/views/text/text_field_builder.dart';
export 'presentation/views/text/text_form_field_builder.dart';
export 'presentation/views/text/text_overflow_builder.dart';
export 'presentation/views/toast/tmail_toast.dart';
export 'presentation/views/toast/toast_position.dart';
export 'utils/app_logger.dart';
export 'utils/benchmark.dart';
export 'utils/build_utils.dart';
export 'utils/config/app_config_loader.dart';
export 'utils/config/app_config_parser.dart';
export 'utils/config/errors.dart';
export 'utils/file_utils.dart';
export 'utils/fps_manager.dart';
export 'utils/html/html_template.dart';
export 'utils/option_param_mixin.dart';
export 'utils/platform_info.dart';
export 'utils/print_utils.dart';
export 'utils/string_convert.dart';
@@ -4,8 +4,8 @@ import 'dart:io';
import 'package:core/data/network/dio_client.dart';
import 'package:core/data/utils/compress_file_utils.dart';
import 'package:core/presentation/utils/html_transformer/html_utils.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
@@ -1,7 +1,7 @@
import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/presentation/utils/html_transformer/html_template.dart';
import 'package:core/utils/html/html_template.dart';
import 'package:html/dom.dart';
class AddTooltipLinkTransformer extends DomTransformer {
@@ -28,9 +28,9 @@ class AddTooltipLinkTransformer extends DomTransformer {
final innerHtml = element.innerHtml;
final tagClass = element.attributes['class'];
if (tagClass != null) {
element.attributes['class'] = '$tagClass $nameClassToolTip';
element.attributes['class'] = '$tagClass ${HtmlTemplate.nameClassToolTip}';
} else {
element.attributes['class'] = nameClassToolTip;
element.attributes['class'] = HtmlTemplate.nameClassToolTip;
}
element.innerHtml = innerHtml + textHasToolTip(url);
}
@@ -1,8 +1,8 @@
import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/presentation/utils/html_transformer/html_template.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_template.dart';
import 'package:html/dom.dart';
class RemoveTooltipLinkTransformer extends DomTransformer {
@@ -15,11 +15,11 @@ class RemoveTooltipLinkTransformer extends DomTransformer {
required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID,
}) async {
final linkElements = document.querySelectorAll('a.$nameClassToolTip');
final linkElements = document.querySelectorAll('a.${HtmlTemplate.nameClassToolTip}');
await Future.wait(linkElements.map((linkElement) async {
final classAttribute = linkElement.attributes['class'];
if (classAttribute != null) {
final newClassAttribute = classAttribute.replaceFirst(nameClassToolTip, '');
final newClassAttribute = classAttribute.replaceFirst(HtmlTemplate.nameClassToolTip, '');
linkElement.attributes['class'] = newClassAttribute;
}
final listSpanTag = linkElement.querySelectorAll('span.tooltiptext');
@@ -1,5 +0,0 @@
class HtmlEventAction {
static const scrollRightEndAction = 'ScrollRightEndAction';
static const scrollLeftEndAction = 'ScrollLeftEndAction';
}
@@ -1,64 +0,0 @@
import 'package:flutter/material.dart';
const nameClassToolTip = 'tmail-tooltip';
const tooltipLinkCss = '''
.$nameClassToolTip .tooltiptext {
visibility: hidden;
max-width: 400px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 8px 5px 8px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
position: absolute;
z-index: 1;
}
.$nameClassToolTip:hover .tooltiptext {
visibility: visible;
}
''';
String generateHtml(String content, {
double? minHeight,
double? minWidth,
String? styleCSS,
String? javaScripts,
bool hideScrollBar = true,
TextDirection? direction
}) {
return '''
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
.tmail-content {
min-height: ${minHeight ?? 0}px;
min-width: ${minWidth ?? 0}px;
overflow: auto;
}
${hideScrollBar ? '''
.tmail-content::-webkit-scrollbar {
display: none;
}
.tmail-content {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
''' : ''}
${styleCSS ?? ''}
</style>
</head>
<body ${direction == TextDirection.rtl ? 'dir="rtl"' : ''} style = "overflow-x: hidden">
<div class="tmail-content">$content</div>
${javaScripts ?? ''}
</body>
</html>
''';
}
@@ -3,12 +3,13 @@ import 'dart:convert';
import 'dart:math' as math;
import 'package:core/presentation/extensions/color_extension.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/shims/dart_ui.dart' as ui;
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_interaction.dart';
import 'package:core/utils/html/html_template.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:universal_html/html.dart' as html;
import 'package:core/presentation/utils/shims/dart_ui.dart' as ui;
class HtmlContentViewerOnWeb extends StatefulWidget {
@@ -162,11 +163,12 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
</script>
''';
final htmlTemplate = generateHtml(content,
final htmlTemplate = HtmlUtils.generateHtmlDocument(
content: content,
minHeight: minHeight,
minWidth: _minWidth,
styleCSS: tooltipLinkCss,
javaScripts: webViewActionScripts + scriptsDisableZoom + HtmlUtils.scriptsHandleLazyLoadingBackgroundImage,
styleCSS: HtmlTemplate.tooltipLinkCss,
javaScripts: webViewActionScripts + scriptsDisableZoom + HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage,
direction: widget.direction);
return htmlTemplate;
@@ -1,9 +1,11 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:core/core.dart';
import 'package:core/presentation/utils/html_transformer/html_event_action.dart';
import 'package:core/presentation/utils/html_transformer/html_utils.dart';
import 'package:core/presentation/views/loading/cupertino_loading_widget.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_interaction.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
@@ -72,9 +74,9 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
};
}
if (PlatformInfo.isAndroid) {
_customScripts = HtmlUtils.scriptsHandleLazyLoadingBackgroundImage + HtmlUtils.scriptsHandleContentSizeChanged;
_customScripts = HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage + HtmlInteraction.scriptsHandleContentSizeChanged;
} else {
_customScripts = HtmlUtils.scriptsHandleLazyLoadingBackgroundImage;
_customScripts = HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage;
}
_initialData();
}
@@ -91,8 +93,8 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
void _initialData() {
_actualHeight = _minHeight;
_htmlData = generateHtml(
widget.contentHtml,
_htmlData = HtmlUtils.generateHtmlDocument(
content: widget.contentHtml,
direction: widget.direction,
javaScripts: _customScripts
);
@@ -138,13 +140,13 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
await controller.loadData(data: _htmlData ?? '');
controller.addJavaScriptHandler(
handlerName: HtmlUtils.scrollEventJSChannelName,
handlerName: HtmlInteraction.scrollEventJSChannelName,
callback: _onHandleScrollEvent
);
if (PlatformInfo.isAndroid) {
controller.addJavaScriptHandler(
handlerName: HtmlUtils.contentSizeChangedEventJSChannelName,
handlerName: HtmlInteraction.contentSizeChangedEventJSChannelName,
callback: _onHandleContentSizeChangedEvent
);
}
@@ -174,9 +176,9 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
void _onHandleScrollEvent(List<dynamic> parameters) {
log('_HtmlContentViewState::_onHandleScrollEvent():parameters: $parameters');
final message = parameters.first;
if (message == HtmlEventAction.scrollLeftEndAction) {
if (message == HtmlInteraction.scrollLeftEndAction) {
widget.onScrollHorizontalEnd?.call(true);
} else if (message == HtmlEventAction.scrollRightEndAction) {
} else if (message == HtmlInteraction.scrollRightEndAction) {
widget.onScrollHorizontalEnd?.call(false);
}
}
@@ -234,7 +236,7 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
}
if (!isScrollActivated) {
await _webViewController.evaluateJavascript(source: HtmlUtils.runScriptsHandleScrollEvent);
await _webViewController.evaluateJavascript(source: HtmlInteraction.runScriptsHandleScrollEvent);
}
widget.onLoadWidthHtmlViewer?.call(isScrollActivated);
}
@@ -1,16 +1,11 @@
import 'package:core/presentation/extensions/html_extension.dart';
import 'package:core/presentation/utils/html_transformer/html_event_action.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
class HtmlInteraction {
static const String scrollRightEndAction = 'ScrollRightEndAction';
static const String scrollLeftEndAction = 'ScrollLeftEndAction';
static const String scrollEventJSChannelName = 'ScrollEventListener';
static const String contentSizeChangedEventJSChannelName = 'ContentSizeChangedEventListener';
class HtmlUtils {
static const scrollEventJSChannelName = 'ScrollEventListener';
static const contentSizeChangedEventJSChannelName = 'ContentSizeChangedEventListener';
static const runScriptsHandleScrollEvent = '''
static const String runScriptsHandleScrollEvent = '''
let contentElement = document.getElementsByClassName('tmail-content')[0];
var xDown = null;
var yDown = null;
@@ -50,11 +45,11 @@ class HtmlUtils {
if (maxOffset === scrollLeftRounded ||
maxOffset === (scrollLeftRounded + 1) ||
maxOffset === (scrollLeftRounded - 1)) {
window.flutter_inappwebview.callHandler('$scrollEventJSChannelName', '${HtmlEventAction.scrollRightEndAction}');
window.flutter_inappwebview.callHandler('$scrollEventJSChannelName', '$scrollRightEndAction');
}
} else {
if (scrollLeftRounded === 0) {
window.flutter_inappwebview.callHandler('$scrollEventJSChannelName', '${HtmlEventAction.scrollLeftEndAction}');
window.flutter_inappwebview.callHandler('$scrollEventJSChannelName', '$scrollLeftEndAction');
}
}
}
@@ -64,7 +59,7 @@ class HtmlUtils {
}
''';
static const scriptsHandleContentSizeChanged = '''
static const String scriptsHandleContentSizeChanged = '''
<script>
const bodyResizeObserver = new ResizeObserver(entries => {
window.flutter_inappwebview.callHandler('$contentSizeChangedEventJSChannelName', '');
@@ -74,7 +69,7 @@ class HtmlUtils {
</script>
''';
static const scriptsHandleLazyLoadingBackgroundImage = '''
static const String scriptsHandleLazyLoadingBackgroundImage = '''
<script>
const lazyImages = document.querySelectorAll('[lazy]');
const lazyImageObserver = new IntersectionObserver((entries, observer) => {
@@ -96,53 +91,4 @@ class HtmlUtils {
});
</script>
''';
static String customCssStyleHtmlEditor({TextDirection direction = TextDirection.ltr}) {
if (PlatformInfo.isWeb) {
return '''
<style>
.note-editable {
direction: ${direction.name};
}
.note-editable .tmail-signature {
text-align: ${direction == TextDirection.rtl ? 'right' : 'left'};
}
</style>
''';
} else if (PlatformInfo.isMobile) {
return '''
#editor {
direction: ${direction.name};
}
#editor .tmail-signature {
text-align: ${direction == TextDirection.rtl ? 'right' : 'left'};
}
''';
} else {
return '';
}
}
static String validateHtmlImageResourceMimeType(String mimeType) {
if (mimeType.endsWith('svg')) {
mimeType = 'image/svg+xml';
}
log('HtmlUtils::validateHtmlImageResourceMimeType:mimeType: $mimeType');
return mimeType;
}
static String convertBase64ToImageResourceData({
required String base64Data,
required String mimeType
}) {
mimeType = validateHtmlImageResourceMimeType(mimeType);
if (!base64Data.endsWith('==')) {
base64Data.append('==');
}
final imageResource = 'data:$mimeType;base64,$base64Data';
log('HtmlUtils::convertBase64ToImageResourceData:imageResource: $imageResource');
return imageResource;
}
}
}
+34
View File
@@ -0,0 +1,34 @@
class HtmlTemplate {
static const String nameClassToolTip = 'tmail-tooltip';
static const String tooltipLinkCss = '''
.$nameClassToolTip .tooltiptext {
visibility: hidden;
max-width: 400px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 8px 5px 8px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
position: absolute;
z-index: 1;
}
.$nameClassToolTip:hover .tooltiptext {
visibility: visible;
}
''';
static const String sampleHtmlDocument = '''
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body</body>
</html>
''';
}
+97
View File
@@ -0,0 +1,97 @@
import 'package:core/presentation/extensions/html_extension.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
class HtmlUtils {
static String customCssStyleHtmlEditor({TextDirection direction = TextDirection.ltr}) {
if (PlatformInfo.isWeb) {
return '''
<style>
.note-editable {
direction: ${direction.name};
}
.note-editable .tmail-signature {
text-align: ${direction == TextDirection.rtl ? 'right' : 'left'};
}
</style>
''';
} else if (PlatformInfo.isMobile) {
return '''
#editor {
direction: ${direction.name};
}
#editor .tmail-signature {
text-align: ${direction == TextDirection.rtl ? 'right' : 'left'};
}
''';
} else {
return '';
}
}
static String validateHtmlImageResourceMimeType(String mimeType) {
if (mimeType.endsWith('svg')) {
mimeType = 'image/svg+xml';
}
log('HtmlUtils::validateHtmlImageResourceMimeType:mimeType: $mimeType');
return mimeType;
}
static String convertBase64ToImageResourceData({
required String base64Data,
required String mimeType
}) {
mimeType = validateHtmlImageResourceMimeType(mimeType);
if (!base64Data.endsWith('==')) {
base64Data.append('==');
}
final imageResource = 'data:$mimeType;base64,$base64Data';
log('HtmlUtils::convertBase64ToImageResourceData:imageResource: $imageResource');
return imageResource;
}
static String generateHtmlDocument({
required String content,
double? minHeight,
double? minWidth,
String? styleCSS,
String? javaScripts,
bool hideScrollBar = true,
TextDirection? direction
}) {
return '''
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
.tmail-content {
min-height: ${minHeight ?? 0}px;
min-width: ${minWidth ?? 0}px;
overflow: auto;
}
${hideScrollBar ? '''
.tmail-content::-webkit-scrollbar {
display: none;
}
.tmail-content {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
''' : ''}
${styleCSS ?? ''}
</style>
</head>
<body ${direction == TextDirection.rtl ? 'dir="rtl"' : ''} style = "overflow-x: hidden">
<div class="tmail-content">$content</div>
${javaScripts ?? ''}
</body>
</html>
''';
}
}
@@ -1,7 +1,7 @@
import 'dart:io';
import 'package:core/presentation/utils/html_transformer/html_utils.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:file_picker/file_picker.dart';
import 'package:rich_text_composer/rich_text_composer.dart';
import 'package:tmail_ui_user/features/composer/presentation/controller/base_rich_text_controller.dart';
@@ -2,8 +2,8 @@
import 'dart:convert';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/html_transformer/html_utils.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:custom_pop_up_menu/custom_pop_up_menu.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
@@ -1,5 +1,5 @@
import 'package:core/presentation/utils/html_transformer/html_utils.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:flutter/material.dart';
import 'package:rich_text_composer/rich_text_composer.dart';
@@ -1,6 +1,6 @@
import 'package:core/presentation/utils/html_transformer/html_utils.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:flutter/material.dart';
import 'package:html_editor_enhanced/html_editor.dart';
@@ -2,10 +2,10 @@ 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/utils/html_transformer/html_utils.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:core/presentation/views/button/icon_button_web.dart';
import 'package:core/presentation/views/responsive/responsive_widget.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -1,6 +1,9 @@
import 'package:core/core.dart';
import 'package:core/presentation/utils/html_transformer/html_utils.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/keyboard_utils.dart';
import 'package:core/presentation/views/button/icon_button_web.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';