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 f560a4a63..05ddb4764 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 @@ -16,6 +16,7 @@ import 'package:universal_html/html.dart' as html; typedef OnClickHyperLinkAction = Function(Uri?); typedef OnMailtoClicked = void Function(Uri? uri); typedef OnIFrameKeyboardShortcutAction = void Function(KeyShortcut keyShortcut); +typedef OnIFrameClickAction = void Function(); class HtmlContentViewerOnWeb extends StatefulWidget { @@ -34,6 +35,8 @@ class HtmlContentViewerOnWeb extends StatefulWidget { final OnIFrameKeyboardShortcutAction? onIFrameKeyboardShortcutAction; + final OnIFrameClickAction? onIFrameClickAction; + // if widthContent is bigger than width of htmlContent, set this to true let widget able to resize to width of htmlContent final bool allowResizeToDocumentSize; @@ -47,6 +50,7 @@ class HtmlContentViewerOnWeb extends StatefulWidget { final double offsetHtmlContentHeight; final double? viewMaxHeight; final bool autoAdjustHeight; + final bool useLinkTooltipOverlay; const HtmlContentViewerOnWeb({ Key? key, @@ -65,12 +69,14 @@ class HtmlContentViewerOnWeb extends StatefulWidget { this.keepAlive = false, this.disableScrolling = false, this.autoAdjustHeight = false, + this.useLinkTooltipOverlay = false, this.fontSize = 14, this.htmlContentMinHeight = ConstantsUI.htmlContentMinHeight, this.htmlContentMinWidth = ConstantsUI.htmlContentMinWidth, this.offsetHtmlContentHeight = ConstantsUI.htmlContentOffsetHeight, this.viewMaxHeight, this.onIFrameKeyboardShortcutAction, + this.onIFrameClickAction, }) : super(key: key); @override @@ -124,6 +130,9 @@ class _HtmlContentViewerOnWebState extends State } else if (_isIframeKeyboardEventTriggered(type)) { _handleOnIFrameKeyboardEvent(data); return; + } else if (_isIframeClickEventTriggered(type)) { + _handleOnIFrameClickEvent(data); + return; } if (data['message'] == iframeOnLoadMessage) { @@ -288,6 +297,20 @@ class _HtmlContentViewerOnWebState extends State } } + bool _isIframeClickEventTriggered(String? type) { + return widget.useLinkTooltipOverlay && + type?.contains('toDart: iframeClick') == true; + } + + void _handleOnIFrameClickEvent(dynamic data) { + try { + log('$runtimeType::_handleOnIFrameClickEvent: $data'); + widget.onIFrameClickAction?.call(); + } catch (e) { + logError('$runtimeType::_handleOnIFrameClickEvent: Exception = $e'); + } + } + @override void didUpdateWidget(covariant HtmlContentViewerOnWeb oldWidget) { super.didUpdateWidget(oldWidget); @@ -431,6 +454,8 @@ class _HtmlContentViewerOnWebState extends State ), if (widget.onIFrameKeyboardShortcutAction != null) HtmlInteraction.scriptHandleIframeKeyboardListener(_createdViewId), + if (widget.useLinkTooltipOverlay) + HtmlInteraction.scriptsHandleIframeClickListener(_createdViewId), ].join(); final htmlTemplate = HtmlUtils.generateHtmlDocument( diff --git a/core/lib/utils/html/html_interaction.dart b/core/lib/utils/html/html_interaction.dart index 0b33f01c7..4b1b78f34 100644 --- a/core/lib/utils/html/html_interaction.dart +++ b/core/lib/utils/html/html_interaction.dart @@ -405,4 +405,18 @@ class HtmlInteraction { } '''; + + static String scriptsHandleIframeClickListener(String viewId) => ''' + + '''; } \ No newline at end of file diff --git a/lib/features/composer/presentation/controller/rich_text_web_controller.dart b/lib/features/composer/presentation/controller/rich_text_web_controller.dart index 79e66c3ee..d4483793d 100644 --- a/lib/features/composer/presentation/controller/rich_text_web_controller.dart +++ b/lib/features/composer/presentation/controller/rich_text_web_controller.dart @@ -325,6 +325,12 @@ class RichTextWebController extends GetxController { bool get isFormattingOptionsEnabled => formattingOptionsState.value == FormattingOptionsState.enabled; + void openInsertLink() { + FocusManager.instance.primaryFocus?.unfocus(); + editorController.setFocus(); + editorController.openInsertLinkDialog(); + } + @override void onClose() { menuParagraphController.dispose(); diff --git a/lib/features/composer/presentation/extensions/handle_insert_link_composer_extension.dart b/lib/features/composer/presentation/extensions/handle_insert_link_composer_extension.dart index 0b6ed3793..495341bd0 100644 --- a/lib/features/composer/presentation/extensions/handle_insert_link_composer_extension.dart +++ b/lib/features/composer/presentation/extensions/handle_insert_link_composer_extension.dart @@ -9,7 +9,7 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; extension HandleInsertLinkComposerExtension on ComposerController { void openInsertLink() { - richTextWebController?.editorController.openInsertLinkDialog(); + richTextWebController?.openInsertLink(); } LinkOverlayOptions createLinkOverlayOptions(BuildContext context) { diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index b053e81ea..8f57b8939 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -19,6 +19,7 @@ import 'package:tmail_ui_user/features/base/widget/optional_expanded.dart'; import 'package:tmail_ui_user/features/base/widget/optional_scroll.dart'; import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_event_extension.dart'; +import 'package:tmail_ui_user/features/email/presentation/extensions/handle_on_iframe_click_in_email_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/validate_display_free_busy_message_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/styles/email_view_styles.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart'; @@ -340,6 +341,7 @@ class EmailView extends GetWidget { presentationEmail: controller.currentEmail, scrollController: scrollController, isInsideThreadDetailView: isInsideThreadDetailView, + onIFrameClickAction: controller.handleOnIFrameClick, )), ], ) @@ -361,8 +363,10 @@ class EmailView extends GetWidget { useDefaultFontStyle: true, scrollController: scrollController, enableQuoteToggle: true, + useLinkTooltipOverlay: true, fontSize: isMobileResponsive ? 16 : 14, onIFrameKeyboardShortcutAction: onIFrameKeyboardShortcutAction, + onIFrameClickAction: controller.handleOnIFrameClick, ), ); } else if (PlatformInfo.isIOS) { diff --git a/lib/features/email/presentation/extensions/handle_on_iframe_click_in_email_extension.dart b/lib/features/email/presentation/extensions/handle_on_iframe_click_in_email_extension.dart new file mode 100644 index 000000000..24c9a8fde --- /dev/null +++ b/lib/features/email/presentation/extensions/handle_on_iframe_click_in_email_extension.dart @@ -0,0 +1,12 @@ +import 'package:core/utils/app_logger.dart'; +import 'package:html_editor_enhanced/html_editor.dart'; +import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart'; + +extension HandleOnIframeClickInEmailExtension on SingleEmailController { + void handleOnIFrameClick() { + log('$runtimeType::handleOnIFrameClick'); + if (LinkOverlayManager.instance.hasActiveOverlays) { + LinkOverlayManager.instance.hideAll(); + } + } +} diff --git a/lib/features/email/presentation/widgets/calendar_event/calendar_event_detail_widget.dart b/lib/features/email/presentation/widgets/calendar_event/calendar_event_detail_widget.dart index c952befdc..9e0887cb7 100644 --- a/lib/features/email/presentation/widgets/calendar_event/calendar_event_detail_widget.dart +++ b/lib/features/email/presentation/widgets/calendar_event/calendar_event_detail_widget.dart @@ -1,3 +1,4 @@ +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:flutter/material.dart'; import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart'; @@ -18,6 +19,7 @@ class CalendarEventDetailWidget extends StatelessWidget { final PresentationEmail? presentationEmail; final ScrollController? scrollController; final bool isInsideThreadDetailView; + final OnIFrameClickAction? onIFrameClickAction; const CalendarEventDetailWidget({ super.key, @@ -27,6 +29,7 @@ class CalendarEventDetailWidget extends StatelessWidget { this.presentationEmail, this.scrollController, this.isInsideThreadDetailView = false, + this.onIFrameClickAction, }); @override @@ -65,6 +68,7 @@ class CalendarEventDetailWidget extends StatelessWidget { onMailtoDelegateAction: onMailtoDelegateAction, scrollController: scrollController, isInsideThreadDetailView: isInsideThreadDetailView, + onIFrameClickAction: onIFrameClickAction, ) ), ], diff --git a/lib/features/email/presentation/widgets/calendar_event/event_body_content_widget.dart b/lib/features/email/presentation/widgets/calendar_event/event_body_content_widget.dart index 795d399f1..0152f09de 100644 --- a/lib/features/email/presentation/widgets/calendar_event/event_body_content_widget.dart +++ b/lib/features/email/presentation/widgets/calendar_event/event_body_content_widget.dart @@ -16,6 +16,7 @@ class EventBodyContentWidget extends StatelessWidget { final OnMailtoDelegateAction? onMailtoDelegateAction; final ScrollController? scrollController; final bool isInsideThreadDetailView; + final OnIFrameClickAction? onIFrameClickAction; const EventBodyContentWidget({ super.key, @@ -23,6 +24,7 @@ class EventBodyContentWidget extends StatelessWidget { this.onMailtoDelegateAction, this.scrollController, this.isInsideThreadDetailView = false, + this.onIFrameClickAction, }); @override @@ -51,9 +53,11 @@ class EventBodyContentWidget extends StatelessWidget { widthContent: constraints.maxWidth, contentHtml: content, useDefaultFontStyle: true, + useLinkTooltipOverlay: true, mailtoDelegate: onMailtoDelegateAction, direction: AppUtils.getCurrentDirection(context), scrollController: scrollController, + onIFrameClickAction: onIFrameClickAction, ); }) )