From c456cde7a84c434d449bd8bb8be264d33898d511 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 5 Apr 2022 18:49:30 +0700 Subject: [PATCH] TF-359 Open a web address and mailto link from EmailView --- core/lib/core.dart | 1 - .../cupertino_action_sheet_builder.dart | 5 +-- .../context_menu/context_menu_builder.dart | 5 +-- .../views/dialog/loading_dialog_builder.dart | 31 ------------------- .../html_content_viewer_on_web_widget.dart | 13 ++++++-- .../html_content_viewer_widget.dart | 12 +++---- ios/Runner/Info.plist | 5 +++ .../presentation/composer_controller.dart | 4 +++ .../email/presentation/email_controller.dart | 27 ++++++++++++++-- .../email/presentation/email_view.dart | 6 ++-- .../email_address_bottom_sheet_builder.dart | 15 ++++----- 11 files changed, 69 insertions(+), 55 deletions(-) delete mode 100644 core/lib/presentation/views/dialog/loading_dialog_builder.dart diff --git a/core/lib/core.dart b/core/lib/core.dart index 2ff976dff..494c63304 100644 --- a/core/lib/core.dart +++ b/core/lib/core.dart @@ -41,7 +41,6 @@ export 'presentation/views/context_menu/context_menu_action_builder.dart'; export 'presentation/views/context_menu/context_menu_builder.dart'; export 'presentation/views/context_menu/context_menu_header_builder.dart'; export 'presentation/views/context_menu/simple_context_menu_action_builder.dart'; -export 'presentation/views/dialog/loading_dialog_builder.dart'; export 'presentation/views/dialog/downloading_file_dialog_builder.dart'; export 'presentation/views/dialog/confirmation_dialog_builder.dart'; export 'presentation/views/dialog/edit_text_dialog_builder.dart'; diff --git a/core/lib/presentation/views/bottom_popup/cupertino_action_sheet_builder.dart b/core/lib/presentation/views/bottom_popup/cupertino_action_sheet_builder.dart index 9f3d830b1..b11f924fb 100644 --- a/core/lib/presentation/views/bottom_popup/cupertino_action_sheet_builder.dart +++ b/core/lib/presentation/views/bottom_popup/cupertino_action_sheet_builder.dart @@ -1,6 +1,7 @@ import 'package:core/core.dart'; import 'package:flutter/cupertino.dart'; +import 'package:pointer_interceptor/pointer_interceptor.dart'; class CupertinoActionSheetBuilder { @@ -33,12 +34,12 @@ class CupertinoActionSheetBuilder { showCupertinoModalPopup( context: _context, barrierColor: AppColor.colorDefaultCupertinoActionSheet, - builder: (context) => CupertinoActionSheet( + builder: (context) => PointerInterceptor(child: CupertinoActionSheet( title: _titleWidget, message: _messageWidget, actions: _actionTiles, cancelButton: _cancelWidget, - ) + )) ); } } \ No newline at end of file diff --git a/core/lib/presentation/views/context_menu/context_menu_builder.dart b/core/lib/presentation/views/context_menu/context_menu_builder.dart index 869253eb0..616617e85 100644 --- a/core/lib/presentation/views/context_menu/context_menu_builder.dart +++ b/core/lib/presentation/views/context_menu/context_menu_builder.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pointer_interceptor/pointer_interceptor.dart'; typedef OnCloseContextMenuAction = void Function(); @@ -54,7 +55,7 @@ class ContextMenuBuilder { void build() { Get.bottomSheet( - GestureDetector( + PointerInterceptor(child: GestureDetector( onTap: () { if (_onCloseContextMenuAction != null) { _onCloseContextMenuAction!(); @@ -88,7 +89,7 @@ class ContextMenuBuilder { ), ), ), - ), + )), useRootNavigator: true, shape: _shape(), isScrollControlled: true, diff --git a/core/lib/presentation/views/dialog/loading_dialog_builder.dart b/core/lib/presentation/views/dialog/loading_dialog_builder.dart deleted file mode 100644 index 01019ae77..000000000 --- a/core/lib/presentation/views/dialog/loading_dialog_builder.dart +++ /dev/null @@ -1,31 +0,0 @@ - -import 'package:core/core.dart'; -import 'package:flutter/cupertino.dart'; - -class LoadingDialogBuilder { - final Key _key; - final String _title; - - LoadingDialogBuilder(this._key, this._title); - - Widget build() { - return CupertinoAlertDialog( - key: _key, - title: Text(_title, style: TextStyle(fontSize: 17.0, color: AppColor.appColor)), - content: Padding( - padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0), - child: Center( - child: Column( - children: [ - SizedBox( - width: 20.0, - height: 20.0, - child: CupertinoActivityIndicator()), - SizedBox(height: 16), - ], - ), - ), - ), - ); - } -} \ No newline at end of file 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 0e136a208..30abc151b 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,12 +16,16 @@ class HtmlContentViewerOnWeb extends StatefulWidget { final double heightContent; final HtmlViewerControllerForWeb controller; + /// Handler for mailto: links + final Function(Uri?)? mailtoDelegate; + const HtmlContentViewerOnWeb({ Key? key, required this.contentHtml, required this.widthContent, required this.heightContent, required this.controller, + this.mailtoDelegate, }) : super(key: key); @override @@ -106,7 +110,7 @@ class _HtmlContentViewerOnWebState extends State { return false; } - return url.protocol === "http:" || url.protocol === "https:"; + return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "mailto:"; } '''; @@ -217,7 +221,12 @@ class _HtmlContentViewerOnWebState extends State { final link = data['url']; if (link != null && mounted) { log('_HtmlContentViewerOnWebState::_setUpWeb(): OpenLink: $link'); - html.window.open('$link', '_blank'); + final urlString = link as String; + if (urlString.startsWith('mailto:')) { + widget.mailtoDelegate?.call(Uri.parse(urlString)); + } else { + html.window.open('$urlString', '_blank'); + } } } }); 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 97c47ba04..1da39c801 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 @@ -23,7 +23,7 @@ class HtmlContentViewer extends StatefulWidget { /// Handler for any non-media URLs that the user taps on the website. /// /// Returns `true` when the given `url` was handled. - final Future Function(String url)? urlLauncherDelegate; + final Future Function(Uri url)? urlLauncherDelegate; const HtmlContentViewer({ Key? key, @@ -180,12 +180,12 @@ class _HtmlContentViewState extends State { final url = navigation.url; final urlDelegate = widget.urlLauncherDelegate; if (urlDelegate != null) { - final handled = await urlDelegate(url); - if (handled) { - return NavigationDecision.prevent; - } + await urlDelegate(Uri.parse(url)); + return NavigationDecision.prevent; + } + if (await launcher.canLaunch(url)) { + await launcher.launch(url); } - await launcher.launch(url); return NavigationDecision.prevent; } } \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index c9241bce5..72bb5a63b 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,6 +2,11 @@ + LSApplicationQueriesSchemes + + https + http + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 2f67c49dd..5a1566b8c 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -260,18 +260,22 @@ class ComposerController extends BaseController { if (listToEmailAddress.isNotEmpty || listCcEmailAddress.isNotEmpty || listBccEmailAddress.isNotEmpty) { isInitialRecipient.value = true; + toAddressExpandMode.value = ExpandMode.COLLAPSE; } if (listCcEmailAddress.isNotEmpty) { listEmailAddressType.add(PrefixEmailAddress.cc); + ccAddressExpandMode.value = ExpandMode.COLLAPSE; } if (listBccEmailAddress.isNotEmpty) { listEmailAddressType.add(PrefixEmailAddress.bcc); + bccAddressExpandMode.value = ExpandMode.COLLAPSE; } } else if (arguments.emailAddress != null) { listToEmailAddress.add(arguments.emailAddress!); isInitialRecipient.value = true; + toAddressExpandMode.value = ExpandMode.COLLAPSE; } _updateStatusEmailSendButton(); } diff --git a/lib/features/email/presentation/email_controller.dart b/lib/features/email/presentation/email_controller.dart index 48d4f1e5e..009cea97c 100644 --- a/lib/features/email/presentation/email_controller.dart +++ b/lib/features/email/presentation/email_controller.dart @@ -484,11 +484,11 @@ class EmailController extends BaseController { showDialog( context: context, barrierColor: AppColor.colorDefaultCupertinoActionSheet, - builder: (BuildContext context) => (EmailAddressDialogBuilder(context, imagePaths, emailAddress) + builder: (BuildContext context) => PointerInterceptor(child: (EmailAddressDialogBuilder(context, imagePaths, emailAddress) ..addOnCloseContextMenuAction(() => popBack()) ..addOnCopyEmailAddressAction((emailAddress) => copyEmailAddress(context, emailAddress)) ..addOnComposeEmailAction((emailAddress) => composeEmailFromEmailAddress(emailAddress))) - .build()); + .build())); } } @@ -518,6 +518,29 @@ class EmailController extends BaseController { } } + void openMailToLink(Uri? uri) { + log('EmailController::openMailToLink(): ${uri.toString()}'); + String address = uri?.path ?? ''; + log('EmailController::openMailToLink(): address: $address'); + if (address.isNotEmpty) { + final emailAddress = EmailAddress(null, address); + final arguments = ComposerArguments( + emailActionType: EmailActionType.composeFromEmailAddress, + emailAddress: emailAddress, + mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role); + if (kIsWeb) { + if (mailboxDashBoardController.dashBoardAction != DashBoardAction.compose) { + mailboxDashBoardController.dispatchDashBoardAction(DashBoardAction.compose, arguments: arguments); + } + if (Get.currentRoute == AppRoutes.EMAIL) { + popBack(); + } + } else { + push(AppRoutes.COMPOSER, arguments: arguments); + } + } + } + void deleteEmailPermanently(BuildContext context, PresentationEmail email) { if (responsiveUtils.isMobile(context)) { (ConfirmationDialogActionSheetBuilder(context) diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index cca7cb462..92dc20cc8 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -536,12 +536,14 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio widthContent: constraints.maxWidth, heightContent: MediaQuery.of(context).size.height, contentHtml: allEmailContents, - controller: HtmlViewerControllerForWeb()); + controller: HtmlViewerControllerForWeb(), + mailtoDelegate: (uri) => emailController.openMailToLink(uri)); } else { return HtmlContentViewer( widthContent: MediaQuery.of(context).size.width, contentHtml: allEmailContents, - loadingWidget: EmailContentPlaceHolderLoading(responsiveUtils: responsiveUtils)); + loadingWidget: EmailContentPlaceHolderLoading(responsiveUtils: responsiveUtils), + mailtoDelegate: (uri) async => emailController.openMailToLink(uri)); } } else { return SizedBox.shrink(); diff --git a/lib/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart b/lib/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart index 19ed3ae2b..921388afc 100644 --- a/lib/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart +++ b/lib/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart @@ -5,6 +5,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:model/model.dart'; +import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; typedef OnCloseBottomSheetAction = void Function(); @@ -56,7 +57,7 @@ class EmailAddressBottomSheetBuilder { void show() { Get.bottomSheet( - GestureDetector( + PointerInterceptor(child: GestureDetector( onTap: () => _onCloseBottomSheetAction?.call(), child: SingleChildScrollView( child: Container( @@ -73,11 +74,11 @@ class EmailAddressBottomSheetBuilder { onPressed: () => _onCloseBottomSheetAction?.call(), icon: SvgPicture.asset(_imagePaths.icCloseMailbox, width: 24, height: 24, fit: BoxFit.fill))), (AvatarBuilder() - ..text('${_emailAddress.asString().characters.first.toUpperCase()}') - ..size(64) - ..addTextStyle(TextStyle(fontWeight: FontWeight.w600, fontSize: 23, color: Colors.white)) - ..avatarColor(_emailAddress.avatarColors)) - .build(), + ..text('${_emailAddress.asString().characters.first.toUpperCase()}') + ..size(64) + ..addTextStyle(TextStyle(fontWeight: FontWeight.w600, fontSize: 23, color: Colors.white)) + ..avatarColor(_emailAddress.avatarColors)) + .build(), if (_emailAddress.displayName.isNotEmpty) Padding( padding: EdgeInsets.only(left: 16, right: 16, top: 16), @@ -125,7 +126,7 @@ class EmailAddressBottomSheetBuilder { )), ), ), - ), + )), useRootNavigator: true, shape: _shape(), isScrollControlled: true,