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
@@ -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>
''';
}
@@ -1,148 +0,0 @@
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 HtmlUtils {
static const scrollEventJSChannelName = 'ScrollEventListener';
static const contentSizeChangedEventJSChannelName = 'ContentSizeChangedEventListener';
static const runScriptsHandleScrollEvent = '''
let contentElement = document.getElementsByClassName('tmail-content')[0];
var xDown = null;
var yDown = null;
contentElement.addEventListener('touchstart', handleTouchStart, false);
contentElement.addEventListener('touchmove', handleTouchMove, false);
function getTouches(evt) {
return evt.touches || evt.originalEvent.touches;
}
function handleTouchStart(evt) {
const firstTouch = getTouches(evt)[0];
xDown = firstTouch.clientX;
yDown = firstTouch.clientY;
}
function handleTouchMove(evt) {
if (!xDown || !yDown) {
return;
}
var xUp = evt.touches[0].clientX;
var yUp = evt.touches[0].clientY;
var xDiff = xDown - xUp;
var yDiff = yDown - yUp;
if (Math.abs(xDiff) > Math.abs(yDiff)) {
let newScrollLeft = contentElement.scrollLeft;
let scrollWidth = contentElement.scrollWidth;
let offsetWidth = contentElement.offsetWidth;
let maxOffset = Math.round(scrollWidth - offsetWidth);
let scrollLeftRounded = Math.round(newScrollLeft);
if (xDiff > 0) {
if (maxOffset === scrollLeftRounded ||
maxOffset === (scrollLeftRounded + 1) ||
maxOffset === (scrollLeftRounded - 1)) {
window.flutter_inappwebview.callHandler('$scrollEventJSChannelName', '${HtmlEventAction.scrollRightEndAction}');
}
} else {
if (scrollLeftRounded === 0) {
window.flutter_inappwebview.callHandler('$scrollEventJSChannelName', '${HtmlEventAction.scrollLeftEndAction}');
}
}
}
xDown = null;
yDown = null;
}
''';
static const scriptsHandleContentSizeChanged = '''
<script>
const bodyResizeObserver = new ResizeObserver(entries => {
window.flutter_inappwebview.callHandler('$contentSizeChangedEventJSChannelName', '');
})
bodyResizeObserver.observe(document.body)
</script>
''';
static const scriptsHandleLazyLoadingBackgroundImage = '''
<script>
const lazyImages = document.querySelectorAll('[lazy]');
const lazyImageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const lazyImage = entry.target;
const src = lazyImage.dataset.src;
lazyImage.tagName.toLowerCase() === 'img'
? lazyImage.src = src
: lazyImage.style.backgroundImage = "url(\'" + src + "\')";
lazyImage.removeAttribute('lazy');
observer.unobserve(lazyImage);
}
});
});
lazyImages.forEach((lazyImage) => {
lazyImageObserver.observe(lazyImage);
});
</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;
}
}