diff --git a/core/lib/presentation/utils/html_transformer/html_event_action.dart b/core/lib/presentation/utils/html_transformer/html_event_action.dart
new file mode 100644
index 000000000..c1093ced7
--- /dev/null
+++ b/core/lib/presentation/utils/html_transformer/html_event_action.dart
@@ -0,0 +1,5 @@
+
+class HtmlEventAction {
+ static const scrollRightEndAction = 'ScrollRightEndAction';
+ static const scrollLeftEndAction = 'ScrollLeftEndAction';
+}
\ 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
new file mode 100644
index 000000000..c187bf6ea
--- /dev/null
+++ b/core/lib/presentation/utils/html_transformer/html_utils.dart
@@ -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;
+ }
+ ''';
+}
diff --git a/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart b/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart
index 083df6c73..23561a07f 100644
--- a/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart
+++ b/core/lib/presentation/views/html_viewer/html_content_viewer_widget.dart
@@ -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 {
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 {
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 {
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()),
- if (Platform.isAndroid)
- Factory(() => ScaleGestureRecognizer()),
+ Factory(() => 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 _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 _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 _onNavigation(NavigationRequest navigation) async {
if (navigation.isForMainFrame && navigation.url == 'about:blank') {
return NavigationDecision.navigate;
diff --git a/lib/features/email/presentation/controller/email_supervisor_controller.dart b/lib/features/email/presentation/controller/email_supervisor_controller.dart
index e691fa898..5c2aa6d69 100644
--- a/lib/features/email/presentation/controller/email_supervisor_controller.dart
+++ b/lib/features/email/presentation/controller/email_supervisor_controller.dart
@@ -21,6 +21,7 @@ class EmailSupervisorController extends BaseController {
final canGetNewerEmail = true.obs;
final canGetOlderEmail = true.obs;
final supportedPageView = RxBool(true);
+ final scrollPhysicsPageView = Rxn();
Rxn get selectedEmail => mailboxDashBoardController.selectedEmail;
Session? get sessionCurrent => mailboxDashBoardController.sessionCurrent;
@@ -34,6 +35,12 @@ class EmailSupervisorController extends BaseController {
}
}
+ @override
+ void onInit() {
+ super.onInit();
+ updateScrollPhysicPageView();
+ }
+
@override
void onClose() {
pageController?.dispose();
@@ -51,6 +58,7 @@ class EmailSupervisorController extends BaseController {
}
void onPageChanged(int index) {
+ updateScrollPhysicPageView();
mailboxDashBoardController.selectedEmail.value = listEmail[index];
}
@@ -61,12 +69,31 @@ class EmailSupervisorController extends BaseController {
void getNewerEmail() {
currentIndexPageView = currentIndexPageView - 1;
- pageController?.jumpToPage(currentIndexPageView);
+ _jumpToPage();
}
void getOlderEmail() {
currentIndexPageView = currentIndexPageView + 1;
- pageController?.jumpToPage(currentIndexPageView);
+ _jumpToPage();
+ }
+
+ void _jumpToPage() {
+ if (BuildUtils.isWeb) {
+ pageController?.jumpToPage(currentIndexPageView);
+ } else {
+ pageController?.animateToPage(
+ currentIndexPageView,
+ duration: const Duration(milliseconds: 500),
+ curve: Curves.easeInToLinear);
+ }
+ }
+
+ void updateScrollPhysicPageView({bool isScrollPageViewActivated = false}) {
+ if (BuildUtils.isWeb || !isScrollPageViewActivated) {
+ scrollPhysicsPageView.value = const NeverScrollableScrollPhysics();
+ } else {
+ scrollPhysicsPageView.value = null;
+ }
}
@override
diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart
index edb63f511..fef55dff1 100644
--- a/lib/features/email/presentation/controller/single_email_controller.dart
+++ b/lib/features/email/presentation/controller/single_email_controller.dart
@@ -1116,4 +1116,20 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
}
}
}
+
+ void toggleScrollPhysicsPagerView(bool leftDirection) {
+ log('SingleEmailController::toggleScrollPhysicsPagerView():leftDirection: $leftDirection');
+ log('SingleEmailController::toggleScrollPhysicsPagerView():canGetOlderEmail: ${emailSupervisorController.canGetOlderEmail.isTrue}');
+ log('SingleEmailController::toggleScrollPhysicsPagerView():canGetNewerEmail: ${emailSupervisorController.canGetNewerEmail.isTrue}');
+
+ if (leftDirection) {
+ if (emailSupervisorController.canGetNewerEmail.isTrue) {
+ emailSupervisorController.getNewerEmail();
+ }
+ } else {
+ if (emailSupervisorController.canGetOlderEmail.isTrue) {
+ emailSupervisorController.getOlderEmail();
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart
index 7a20ce0ab..1bd9bb77f 100644
--- a/lib/features/email/presentation/email_view.dart
+++ b/lib/features/email/presentation/email_view.dart
@@ -1,4 +1,17 @@
-import 'package:core/core.dart';
+import 'package:core/domain/extensions/datetime_extension.dart';
+import 'package:core/presentation/extensions/color_extension.dart';
+import 'package:core/presentation/resources/image_paths.dart';
+import 'package:core/presentation/state/success.dart';
+import 'package:core/presentation/utils/icon_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';
+import 'package:core/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart';
+import 'package:core/presentation/views/html_viewer/html_content_viewer_widget.dart';
+import 'package:core/presentation/views/html_viewer/html_viewer_controller_for_web.dart';
+import 'package:core/presentation/views/image/avatar_builder.dart';
+import 'package:core/utils/app_logger.dart';
+import 'package:core/utils/build_utils.dart';
import 'package:filesize/filesize.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -102,18 +115,19 @@ class EmailView extends GetWidget
}
Widget _buildMultipleEmailView(List listEmails) {
- log('EmailView::_buildMultipleEmailView(): ');
- return PageView.builder(
- physics: BuildUtils.isWeb ? const NeverScrollableScrollPhysics() : null,
- itemCount: listEmails.length,
- controller: controller.emailSupervisorController.pageController,
- onPageChanged: controller.emailSupervisorController.onPageChanged,
- itemBuilder: (context, index) => _buildSingleEmailView(context, listEmails[index])
+ return Obx(
+ () => PageView.builder(
+ physics: controller.emailSupervisorController.scrollPhysicsPageView.value,
+ itemCount: listEmails.length,
+ allowImplicitScrolling: true,
+ controller: controller.emailSupervisorController.pageController,
+ onPageChanged: controller.emailSupervisorController.onPageChanged,
+ itemBuilder: (context, index) => _buildSingleEmailView(context, listEmails[index])
+ ),
);
}
Widget _buildSingleEmailView(BuildContext context, PresentationEmail email) {
- log('EmailView::_buildSingleEmailView(): ');
return _buildEmailBody(context, email);
}
@@ -211,6 +225,7 @@ class EmailView extends GetWidget
return _buildEmailMessage(context, email);
} else {
return SingleChildScrollView(
+ primary: true,
physics : const ClampingScrollPhysics(),
child: Container(
margin: EdgeInsets.zero,
@@ -748,9 +763,15 @@ class EmailView extends GetWidget
mailtoDelegate: (uri) => controller.openMailToLink(uri));
} else {
return HtmlContentViewer(
- heightContent: responsiveUtils.getSizeScreenHeight(context),
- contentHtml: allEmailContents,
- mailtoDelegate: (uri) async => controller.openMailToLink(uri));
+ heightContent: responsiveUtils.getSizeScreenHeight(context),
+ contentHtml: allEmailContents,
+ mailtoDelegate: (uri) async => controller.openMailToLink(uri),
+ onScrollHorizontalEnd: controller.toggleScrollPhysicsPagerView,
+ onWebViewLoaded: (isScrollPageViewActivated) {
+ log('EmailView::_buildEmailContent(): isScrollPageViewActivated: $isScrollPageViewActivated');
+ controller.emailSupervisorController.updateScrollPhysicPageView(isScrollPageViewActivated: isScrollPageViewActivated);
+ },
+ );
}
} else {
return const SizedBox.shrink();