TF-1203 Fix can not scroll horizontal email content

This commit is contained in:
dab246
2022-11-29 13:40:28 +07:00
committed by Dat H. Pham
parent 430171de53
commit d088b8b962
6 changed files with 247 additions and 49 deletions
@@ -0,0 +1,5 @@
class HtmlEventAction {
static const scrollRightEndAction = 'ScrollRightEndAction';
static const scrollLeftEndAction = 'ScrollLeftEndAction';
}
@@ -0,0 +1,68 @@
import 'package:core/presentation/utils/html_transformer/html_event_action.dart';
class HtmlUtils {
static const scrollEventJSChannelName = 'ScrollEventListener';
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);
/*
console.log('newScrollLeft: ' + newScrollLeft);
console.log('scrollWidth: ' + scrollWidth);
console.log('offsetWidth: ' + offsetWidth);
console.log('maxOffset: ' + maxOffset);
console.log('scrollLeftRounded: ' + scrollLeftRounded); */
if (xDiff > 0) {
if (maxOffset === scrollLeftRounded ||
maxOffset === (scrollLeftRounded + 1) ||
maxOffset === (scrollLeftRounded - 1)) {
window.$scrollEventJSChannelName.postMessage('${HtmlEventAction.scrollRightEndAction}');
}
} else {
if (scrollLeftRounded === 0) {
window.$scrollEventJSChannelName.postMessage('${HtmlEventAction.scrollLeftEndAction}');
}
}
}
xDown = null;
yDown = null;
}
''';
}
@@ -3,6 +3,8 @@ import 'dart:io';
import 'dart:ui';
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:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
@@ -10,12 +12,16 @@ import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart' as launcher;
import 'package:url_launcher/url_launcher_string.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'dart:developer' as developer;
typedef OnScrollHorizontalEnd = Function(bool leftDirection);
typedef OnWebViewLoaded = Function(bool isScrollPageViewActivated);
class HtmlContentViewer extends StatefulWidget {
final String contentHtml;
final double heightContent;
final OnScrollHorizontalEnd? onScrollHorizontalEnd;
final OnWebViewLoaded? onWebViewLoaded;
/// Register this callback if you want a reference to the [WebViewController].
final void Function(WebViewController controller)? onCreated;
@@ -33,6 +39,8 @@ class HtmlContentViewer extends StatefulWidget {
required this.contentHtml,
required this.heightContent,
this.onCreated,
this.onWebViewLoaded,
this.onScrollHorizontalEnd,
this.urlLauncherDelegate,
this.mailtoDelegate,
}) : super(key: key);
@@ -56,13 +64,7 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
super.initState();
actualHeight = widget.heightContent;
maxHeightForAndroid = window.physicalSize.height;
log('_HtmlContentViewState::initState(): maxHeightForAndroid: $maxHeightForAndroid');
_htmlData = _generateHtmlDocument(widget.contentHtml);
}
String _generateHtmlDocument(String content) {
final htmlTemplate = generateHtml(content);
return htmlTemplate;
_htmlData = generateHtml(widget.contentHtml);
}
@override
@@ -74,7 +76,11 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
height: actualHeight,
width: constraints.maxWidth,
child: _buildWebView()),
if (_isLoading) Align(alignment: Alignment.center, child: _buildLoadingView())
if (_isLoading)
Align(
alignment: Alignment.center,
child: _buildLoadingView()
)
],
);
});
@@ -103,40 +109,95 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
await controller.loadHtmlString(htmlData, baseUrl: null);
widget.onCreated?.call(controller);
},
onPageFinished: (url) async {
final scrollHeightText = await _webViewController.runJavascriptReturningResult('document.body.scrollHeight');
final scrollHeight = double.tryParse(scrollHeightText);
developer.log('onPageFinished(): scrollHeightText: $scrollHeightText', name: 'HtmlContentViewer');
if ((scrollHeight != null) && mounted) {
final scrollHeightWithBuffer = scrollHeight + 30.0;
if (scrollHeightWithBuffer > minHeight) {
setState(() {
//TODO: It hotfix for web_view crash on android device and waiting lib web_view update to fix this issue
if (Platform.isAndroid && scrollHeightWithBuffer > maxHeightForAndroid){
actualHeight = maxHeightForAndroid;
} else {
actualHeight = scrollHeightWithBuffer;
}
_isLoading = false;
});
}
}
if (mounted && _isLoading) {
setState(() {
_isLoading = false;
});
}
},
onPageFinished: _onPageFinished,
zoomEnabled: false,
navigationDelegate: _onNavigation,
gestureRecognizers: {
Factory<LongPressGestureRecognizer>(() => LongPressGestureRecognizer()),
if (Platform.isAndroid)
Factory<ScaleGestureRecognizer>(() => ScaleGestureRecognizer()),
Factory<ScaleGestureRecognizer>(() => ScaleGestureRecognizer()),
},
javascriptChannels: {
JavascriptChannel(
name: HtmlUtils.scrollEventJSChannelName,
onMessageReceived: _onHandleScrollEvent
)
},
gestureNavigationEnabled: true,
debuggingEnabled: true,
);
}
void _onPageFinished(String url) async {
await Future.wait([
_webViewController.runJavascript(HtmlUtils.runScriptsHandleScrollEvent),
_setActualHeightView(),
_setActualWidthView(),
]);
_hideLoadingProgress();
}
void _onHandleScrollEvent(JavascriptMessage javascriptMessage) {
log('_HtmlContentViewState::_onHandleScrollEvent():message: ${javascriptMessage.message}');
if (javascriptMessage.message == HtmlEventAction.scrollRightEndAction) {
widget.onScrollHorizontalEnd?.call(false);
} else if (javascriptMessage.message == HtmlEventAction.scrollLeftEndAction) {
widget.onScrollHorizontalEnd?.call(true);
}
}
Future<void> _setActualHeightView() async {
final scrollHeightText = await _webViewController.runJavascriptReturningResult('document.body.scrollHeight');
final scrollHeight = double.tryParse(scrollHeightText);
log('_HtmlContentViewState::_setActualHeightView(): scrollHeightText: $scrollHeightText');
if (scrollHeight != null && mounted) {
final scrollHeightWithBuffer = scrollHeight + 30.0;
if (scrollHeightWithBuffer > minHeight) {
setState(() {
// It hotfix for web_view crash on android device and waiting lib web_view update to fix this issue
if (Platform.isAndroid && scrollHeightWithBuffer > maxHeightForAndroid){
actualHeight = maxHeightForAndroid;
} else {
actualHeight = scrollHeightWithBuffer;
}
_isLoading = false;
});
}
}
return Future.value(null);
}
Future<void> _setActualWidthView() async {
final result = await Future.wait([
_webViewController.runJavascriptReturningResult('document.getElementsByClassName("tmail-content")[0].scrollWidth'),
_webViewController.runJavascriptReturningResult('document.getElementsByClassName("tmail-content")[0].offsetWidth')
]);
if (result.length == 2) {
final scrollWidth = double.tryParse(result[0]);
final offsetWidth = double.tryParse(result[1]);
log('_HtmlContentViewState::_setActualWidthView():scrollWidth: $scrollWidth');
log('_HtmlContentViewState::_setActualWidthView():offsetWidth: $offsetWidth');
if (scrollWidth != null && offsetWidth != null && mounted) {
final isScrollActivated = scrollWidth.round() == offsetWidth.round();
log('_HtmlContentViewState::_setActualWidthView():isScrollActivated: $isScrollActivated');
widget.onWebViewLoaded?.call(isScrollActivated);
}
}
return Future.value(null);
}
void _hideLoadingProgress() {
if (mounted && _isLoading) {
setState(() {
_isLoading = false;
});
}
}
FutureOr<NavigationDecision> _onNavigation(NavigationRequest navigation) async {
if (navigation.isForMainFrame && navigation.url == 'about:blank') {
return NavigationDecision.navigate;