TF-3644 Handle scroll thread detail when cursor is in email content area
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -32,6 +32,7 @@ class HtmlContentViewerOnWeb extends StatefulWidget {
|
|||||||
final bool allowResizeToDocumentSize;
|
final bool allowResizeToDocumentSize;
|
||||||
|
|
||||||
final bool keepWidthWhileLoading;
|
final bool keepWidthWhileLoading;
|
||||||
|
final ScrollController? scrollController;
|
||||||
|
|
||||||
const HtmlContentViewerOnWeb({
|
const HtmlContentViewerOnWeb({
|
||||||
Key? key,
|
Key? key,
|
||||||
@@ -45,6 +46,7 @@ class HtmlContentViewerOnWeb extends StatefulWidget {
|
|||||||
this.onClickHyperLinkAction,
|
this.onClickHyperLinkAction,
|
||||||
this.keepWidthWhileLoading = false,
|
this.keepWidthWhileLoading = false,
|
||||||
this.contentPadding,
|
this.contentPadding,
|
||||||
|
this.scrollController,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -65,10 +67,11 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
String? _htmlData;
|
String? _htmlData;
|
||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
double minHeight = 100;
|
double minHeight = 100;
|
||||||
late final StreamSubscription<html.MessageEvent> sizeListener;
|
late final StreamSubscription<html.MessageEvent> _onMessageSubscription;
|
||||||
bool _iframeLoaded = false;
|
bool _iframeLoaded = false;
|
||||||
static const String iframeOnLoadMessage = 'iframeHasBeenLoaded';
|
static const String iframeOnLoadMessage = 'iframeHasBeenLoaded';
|
||||||
static const String onClickHyperLinkName = 'onClickHyperLink';
|
static const String onClickHyperLinkName = 'onClickHyperLink';
|
||||||
|
static const String onScrollChangedEvent = 'onScrollChanged';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -77,64 +80,112 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
_actualWidth = widget.widthContent;
|
_actualWidth = widget.widthContent;
|
||||||
_createdViewId = _getRandString(10);
|
_createdViewId = _getRandString(10);
|
||||||
_setUpWeb();
|
_setUpWeb();
|
||||||
|
_onMessageSubscription = html.window.onMessage.listen(_handleMessageEvent);
|
||||||
|
}
|
||||||
|
|
||||||
sizeListener = html.window.onMessage.listen((event) {
|
void _handleMessageEvent(html.MessageEvent event) {
|
||||||
var data = json.decode(event.data);
|
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) {
|
if (data['message'] == iframeOnLoadMessage) {
|
||||||
_iframeLoaded = true;
|
_iframeLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_iframeLoaded) return;
|
if (!_iframeLoaded) return;
|
||||||
|
|
||||||
if (data['type'] != null && data['type'].contains('toDart: htmlHeight')) {
|
if (_isHtmlContentHeightEventTriggered(type)) {
|
||||||
final docHeight = data['height'] ?? _actualHeight;
|
_handleContentHeightEvent(data['height']);
|
||||||
if (docHeight != null && mounted) {
|
} else if (_isHtmlContentWidthEventTriggered(type)) {
|
||||||
final scrollHeightWithBuffer = docHeight + 30.0;
|
_handleContentWidthEvent(data['width']);
|
||||||
if (scrollHeightWithBuffer > minHeight) {
|
} else if (_isMailtoLinkEventTriggered(type)) {
|
||||||
setState(() {
|
_handleMailtoLinkEvent(data['url']);
|
||||||
_actualHeight = scrollHeightWithBuffer;
|
} else if (_isHyperLinkEventTriggered(type)) {
|
||||||
_isLoading = false;
|
_handleHyperLinkEvent(data['url']);
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mounted && _isLoading) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logError('_HtmlContentViewerOnWebState::_handleMessageEvent:Exception = $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (data['type'] != null && data['type'].contains('toDart: htmlWidth') && !widget.keepWidthWhileLoading) {
|
bool _isScrollChangedEventTriggered(String? type) {
|
||||||
final docWidth = data['width'] ?? _actualWidth;
|
return widget.scrollController != null &&
|
||||||
if (docWidth != null && mounted) {
|
type?.contains('toDart: $onScrollChangedEvent') == true;
|
||||||
if (docWidth > _minWidth && widget.allowResizeToDocumentSize) {
|
}
|
||||||
setState(() {
|
|
||||||
_actualWidth = docWidth;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data['type'] != null && data['type'].contains('toDart: OpenLink')) {
|
void _handleIframeOnScrollChangedListener(
|
||||||
final link = data['url'];
|
dynamic data,
|
||||||
if (link != null && mounted) {
|
ScrollController controller,
|
||||||
final urlString = link as String;
|
) {
|
||||||
if (urlString.startsWith('mailto:')) {
|
final deltaY = data['deltaY'] ?? 0.0;
|
||||||
widget.mailtoDelegate?.call(Uri.parse(urlString));
|
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')) {
|
bool _isHtmlContentHeightEventTriggered(String? type) =>
|
||||||
final link = data['url'] as String?;
|
type?.contains('toDart: htmlHeight') == true;
|
||||||
if (link != null && mounted) {
|
|
||||||
widget.onClickHyperLinkAction?.call(Uri.parse(link));
|
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
|
@override
|
||||||
@@ -246,6 +297,17 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
|
|
||||||
resizeObserver.observe(document.body);
|
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
|
||||||
|
}), "*");
|
||||||
|
});
|
||||||
|
''' : ''}
|
||||||
</script>
|
</script>
|
||||||
''';
|
''';
|
||||||
|
|
||||||
@@ -362,7 +424,7 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_htmlData = null;
|
_htmlData = null;
|
||||||
sizeListener.cancel();
|
_onMessageSubscription.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,6 +56,7 @@ class EmailView extends GetWidget<SingleEmailController> {
|
|||||||
this.isFirstEmailInThreadDetail = false,
|
this.isFirstEmailInThreadDetail = false,
|
||||||
this.threadSubject,
|
this.threadSubject,
|
||||||
this.onToggleThreadDetailCollapseExpand,
|
this.onToggleThreadDetailCollapseExpand,
|
||||||
|
this.scrollController,
|
||||||
});
|
});
|
||||||
|
|
||||||
final bool isInsideThreadDetailView;
|
final bool isInsideThreadDetailView;
|
||||||
@@ -63,7 +64,8 @@ class EmailView extends GetWidget<SingleEmailController> {
|
|||||||
final bool isFirstEmailInThreadDetail;
|
final bool isFirstEmailInThreadDetail;
|
||||||
final String? threadSubject;
|
final String? threadSubject;
|
||||||
final VoidCallback? onToggleThreadDetailCollapseExpand;
|
final VoidCallback? onToggleThreadDetailCollapseExpand;
|
||||||
|
final ScrollController? scrollController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? get tag => emailId?.id.value;
|
String? get tag => emailId?.id.value;
|
||||||
|
|
||||||
@@ -388,6 +390,7 @@ class EmailView extends GetWidget<SingleEmailController> {
|
|||||||
direction: AppUtils.getCurrentDirection(context),
|
direction: AppUtils.getCurrentDirection(context),
|
||||||
contentPadding: 0,
|
contentPadding: 0,
|
||||||
useDefaultFont: true,
|
useDefaultFont: true,
|
||||||
|
scrollController: scrollController,
|
||||||
),
|
),
|
||||||
if (controller.mailboxDashBoardController.isDisplayedOverlayViewOnIFrame)
|
if (controller.mailboxDashBoardController.isDisplayedOverlayViewOnIFrame)
|
||||||
PointerInterceptor(
|
PointerInterceptor(
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ extension GetThreadDetailEmailViews on ThreadDetailController {
|
|||||||
onToggleThreadDetailCollapseExpand: () {
|
onToggleThreadDetailCollapseExpand: () {
|
||||||
toggleThreadDetailCollapeExpand(presentationEmail);
|
toggleThreadDetailCollapeExpand(presentationEmail);
|
||||||
},
|
},
|
||||||
|
scrollController: scrollController,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|||||||
Reference in New Issue
Block a user