diff --git a/core/lib/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart b/core/lib/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart
index 4a7b6246f..a29f127fe 100644
--- a/core/lib/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart
+++ b/core/lib/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart
@@ -32,6 +32,7 @@ class HtmlContentViewerOnWeb extends StatefulWidget {
final bool allowResizeToDocumentSize;
final bool keepWidthWhileLoading;
+ final ScrollController? scrollController;
const HtmlContentViewerOnWeb({
Key? key,
@@ -45,6 +46,7 @@ class HtmlContentViewerOnWeb extends StatefulWidget {
this.onClickHyperLinkAction,
this.keepWidthWhileLoading = false,
this.contentPadding,
+ this.scrollController,
}) : super(key: key);
@override
@@ -65,10 +67,11 @@ class _HtmlContentViewerOnWebState extends State {
String? _htmlData;
bool _isLoading = true;
double minHeight = 100;
- late final StreamSubscription sizeListener;
+ late final StreamSubscription _onMessageSubscription;
bool _iframeLoaded = false;
static const String iframeOnLoadMessage = 'iframeHasBeenLoaded';
static const String onClickHyperLinkName = 'onClickHyperLink';
+ static const String onScrollChangedEvent = 'onScrollChanged';
@override
void initState() {
@@ -77,64 +80,112 @@ class _HtmlContentViewerOnWebState extends State {
_actualWidth = widget.widthContent;
_createdViewId = _getRandString(10);
_setUpWeb();
+ _onMessageSubscription = html.window.onMessage.listen(_handleMessageEvent);
+ }
- sizeListener = html.window.onMessage.listen((event) {
- var data = json.decode(event.data);
+ void _handleMessageEvent(html.MessageEvent event) {
+ try {
+ final data = json.decode(event.data);
- if (data['view'] != _createdViewId) return;
+ final viewId = data['view'];
+ if (viewId != _createdViewId) return;
+
+ final type = data['type'];
+ if (_isScrollChangedEventTriggered(type)) {
+ _handleIframeOnScrollChangedListener(data, widget.scrollController!);
+ return;
+ }
if (data['message'] == iframeOnLoadMessage) {
_iframeLoaded = true;
}
-
if (!_iframeLoaded) return;
- if (data['type'] != null && data['type'].contains('toDart: htmlHeight')) {
- final docHeight = data['height'] ?? _actualHeight;
- if (docHeight != null && mounted) {
- final scrollHeightWithBuffer = docHeight + 30.0;
- if (scrollHeightWithBuffer > minHeight) {
- setState(() {
- _actualHeight = scrollHeightWithBuffer;
- _isLoading = false;
- });
- }
- }
- if (mounted && _isLoading) {
- setState(() {
- _isLoading = false;
- });
- }
+ if (_isHtmlContentHeightEventTriggered(type)) {
+ _handleContentHeightEvent(data['height']);
+ } else if (_isHtmlContentWidthEventTriggered(type)) {
+ _handleContentWidthEvent(data['width']);
+ } else if (_isMailtoLinkEventTriggered(type)) {
+ _handleMailtoLinkEvent(data['url']);
+ } else if (_isHyperLinkEventTriggered(type)) {
+ _handleHyperLinkEvent(data['url']);
}
+ } catch (e) {
+ logError('_HtmlContentViewerOnWebState::_handleMessageEvent:Exception = $e');
+ }
+ }
- if (data['type'] != null && data['type'].contains('toDart: htmlWidth') && !widget.keepWidthWhileLoading) {
- final docWidth = data['width'] ?? _actualWidth;
- if (docWidth != null && mounted) {
- if (docWidth > _minWidth && widget.allowResizeToDocumentSize) {
- setState(() {
- _actualWidth = docWidth;
- });
- }
- }
- }
+ bool _isScrollChangedEventTriggered(String? type) {
+ return widget.scrollController != null &&
+ type?.contains('toDart: $onScrollChangedEvent') == true;
+ }
- if (data['type'] != null && data['type'].contains('toDart: OpenLink')) {
- final link = data['url'];
- if (link != null && mounted) {
- final urlString = link as String;
- if (urlString.startsWith('mailto:')) {
- widget.mailtoDelegate?.call(Uri.parse(urlString));
- }
- }
- }
+ void _handleIframeOnScrollChangedListener(
+ dynamic data,
+ ScrollController controller,
+ ) {
+ final deltaY = data['deltaY'] ?? 0.0;
+ final newOffset = controller.offset + deltaY;
+ log('_HtmlContentViewerOnWebState::_handleIframeOnScrollChangedListener:deltaY = $deltaY | newOffset = $newOffset');
+ if (newOffset < controller.position.minScrollExtent) {
+ controller.jumpTo(controller.position.minScrollExtent);
+ } else if (newOffset > controller.position.maxScrollExtent) {
+ controller.jumpTo(controller.position.maxScrollExtent);
+ } else {
+ controller.jumpTo(newOffset);
+ }
+ }
- if (data['type'] != null && data['type'].contains('toDart: $onClickHyperLinkName')) {
- final link = data['url'] as String?;
- if (link != null && mounted) {
- widget.onClickHyperLinkAction?.call(Uri.parse(link));
- }
+ bool _isHtmlContentHeightEventTriggered(String? type) =>
+ type?.contains('toDart: htmlHeight') == true;
+
+ void _handleContentHeightEvent(dynamic height) {
+ final docHeight = height ?? _actualHeight;
+ if (docHeight != null && mounted) {
+ final scrollHeightWithBuffer = docHeight + 30.0;
+ if (scrollHeightWithBuffer > minHeight) {
+ setState(() {
+ _actualHeight = scrollHeightWithBuffer;
+ _isLoading = false;
+ });
}
- });
+ }
+ if (mounted && _isLoading) {
+ setState(() {
+ _isLoading = false;
+ });
+ }
+ }
+
+ bool _isHtmlContentWidthEventTriggered(String? type) =>
+ type?.contains('toDart: htmlWidth') == true &&
+ !widget.keepWidthWhileLoading;
+
+ void _handleContentWidthEvent(dynamic width) {
+ final docWidth = width ?? _actualWidth;
+ if (docWidth != null && mounted &&
+ docWidth > _minWidth &&
+ widget.allowResizeToDocumentSize) {
+ setState(() => _actualWidth = docWidth);
+ }
+ }
+
+ bool _isMailtoLinkEventTriggered(String? type) =>
+ type?.contains('toDart: OpenLink') == true;
+
+ void _handleMailtoLinkEvent(dynamic url) {
+ if (url != null && mounted && url is String && url.startsWith('mailto:')) {
+ widget.mailtoDelegate?.call(Uri.tryParse(url));
+ }
+ }
+
+ bool _isHyperLinkEventTriggered(String? type) =>
+ type?.contains('toDart: $onClickHyperLinkName') == true;
+
+ void _handleHyperLinkEvent(dynamic url) {
+ if (url != null && mounted && url is String) {
+ widget.onClickHyperLinkAction?.call(Uri.tryParse(url));
+ }
}
@override
@@ -246,6 +297,17 @@ class _HtmlContentViewerOnWebState extends State {
resizeObserver.observe(document.body);
}
+
+ ${widget.scrollController != null ? '''
+ window.addEventListener('wheel', function (event) {
+ const deltaY = event.deltaY;
+ window.parent.postMessage(JSON.stringify({
+ "view": "$_createdViewId",
+ "type": "toDart: $onScrollChangedEvent",
+ "deltaY": deltaY
+ }), "*");
+ });
+ ''' : ''}
''';
@@ -362,7 +424,7 @@ class _HtmlContentViewerOnWebState extends State {
@override
void dispose() {
_htmlData = null;
- sizeListener.cancel();
+ _onMessageSubscription.cancel();
super.dispose();
}
}
\ 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 f842101e9..e45f393a1 100644
--- a/lib/features/email/presentation/email_view.dart
+++ b/lib/features/email/presentation/email_view.dart
@@ -56,6 +56,7 @@ class EmailView extends GetWidget {
this.isFirstEmailInThreadDetail = false,
this.threadSubject,
this.onToggleThreadDetailCollapseExpand,
+ this.scrollController,
});
final bool isInsideThreadDetailView;
@@ -63,7 +64,8 @@ class EmailView extends GetWidget {
final bool isFirstEmailInThreadDetail;
final String? threadSubject;
final VoidCallback? onToggleThreadDetailCollapseExpand;
-
+ final ScrollController? scrollController;
+
@override
String? get tag => emailId?.id.value;
@@ -388,6 +390,7 @@ class EmailView extends GetWidget {
direction: AppUtils.getCurrentDirection(context),
contentPadding: 0,
useDefaultFont: true,
+ scrollController: scrollController,
),
if (controller.mailboxDashBoardController.isDisplayedOverlayViewOnIFrame)
PointerInterceptor(
diff --git a/lib/features/thread_detail/presentation/extension/get_thread_details_email_views.dart b/lib/features/thread_detail/presentation/extension/get_thread_details_email_views.dart
index 4da6a542e..e6f6c9205 100644
--- a/lib/features/thread_detail/presentation/extension/get_thread_details_email_views.dart
+++ b/lib/features/thread_detail/presentation/extension/get_thread_details_email_views.dart
@@ -82,6 +82,7 @@ extension GetThreadDetailEmailViews on ThreadDetailController {
onToggleThreadDetailCollapseExpand: () {
toggleThreadDetailCollapeExpand(presentationEmail);
},
+ scrollController: scrollController,
),
);
}).toList();