TF-359 Open a web address and mailto link from EmailView
This commit is contained in:
@@ -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_builder.dart';
|
||||||
export 'presentation/views/context_menu/context_menu_header_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/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/downloading_file_dialog_builder.dart';
|
||||||
export 'presentation/views/dialog/confirmation_dialog_builder.dart';
|
export 'presentation/views/dialog/confirmation_dialog_builder.dart';
|
||||||
export 'presentation/views/dialog/edit_text_dialog_builder.dart';
|
export 'presentation/views/dialog/edit_text_dialog_builder.dart';
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
|
|
||||||
class CupertinoActionSheetBuilder {
|
class CupertinoActionSheetBuilder {
|
||||||
|
|
||||||
@@ -33,12 +34,12 @@ class CupertinoActionSheetBuilder {
|
|||||||
showCupertinoModalPopup(
|
showCupertinoModalPopup(
|
||||||
context: _context,
|
context: _context,
|
||||||
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
|
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
|
||||||
builder: (context) => CupertinoActionSheet(
|
builder: (context) => PointerInterceptor(child: CupertinoActionSheet(
|
||||||
title: _titleWidget,
|
title: _titleWidget,
|
||||||
message: _messageWidget,
|
message: _messageWidget,
|
||||||
actions: _actionTiles,
|
actions: _actionTiles,
|
||||||
cancelButton: _cancelWidget,
|
cancelButton: _cancelWidget,
|
||||||
)
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
|
|
||||||
typedef OnCloseContextMenuAction = void Function();
|
typedef OnCloseContextMenuAction = void Function();
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ class ContextMenuBuilder {
|
|||||||
|
|
||||||
void build() {
|
void build() {
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
GestureDetector(
|
PointerInterceptor(child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (_onCloseContextMenuAction != null) {
|
if (_onCloseContextMenuAction != null) {
|
||||||
_onCloseContextMenuAction!();
|
_onCloseContextMenuAction!();
|
||||||
@@ -88,7 +89,7 @@ class ContextMenuBuilder {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
)),
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
shape: _shape(),
|
shape: _shape(),
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
|
|||||||
@@ -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),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,12 +16,16 @@ class HtmlContentViewerOnWeb extends StatefulWidget {
|
|||||||
final double heightContent;
|
final double heightContent;
|
||||||
final HtmlViewerControllerForWeb controller;
|
final HtmlViewerControllerForWeb controller;
|
||||||
|
|
||||||
|
/// Handler for mailto: links
|
||||||
|
final Function(Uri?)? mailtoDelegate;
|
||||||
|
|
||||||
const HtmlContentViewerOnWeb({
|
const HtmlContentViewerOnWeb({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.contentHtml,
|
required this.contentHtml,
|
||||||
required this.widthContent,
|
required this.widthContent,
|
||||||
required this.heightContent,
|
required this.heightContent,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
|
this.mailtoDelegate,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -106,7 +110,7 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return url.protocol === "http:" || url.protocol === "https:";
|
return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "mailto:";
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
''';
|
''';
|
||||||
@@ -217,7 +221,12 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
final link = data['url'];
|
final link = data['url'];
|
||||||
if (link != null && mounted) {
|
if (link != null && mounted) {
|
||||||
log('_HtmlContentViewerOnWebState::_setUpWeb(): OpenLink: $link');
|
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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class HtmlContentViewer extends StatefulWidget {
|
|||||||
/// Handler for any non-media URLs that the user taps on the website.
|
/// Handler for any non-media URLs that the user taps on the website.
|
||||||
///
|
///
|
||||||
/// Returns `true` when the given `url` was handled.
|
/// Returns `true` when the given `url` was handled.
|
||||||
final Future<bool> Function(String url)? urlLauncherDelegate;
|
final Future<bool> Function(Uri url)? urlLauncherDelegate;
|
||||||
|
|
||||||
const HtmlContentViewer({
|
const HtmlContentViewer({
|
||||||
Key? key,
|
Key? key,
|
||||||
@@ -180,12 +180,12 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
final url = navigation.url;
|
final url = navigation.url;
|
||||||
final urlDelegate = widget.urlLauncherDelegate;
|
final urlDelegate = widget.urlLauncherDelegate;
|
||||||
if (urlDelegate != null) {
|
if (urlDelegate != null) {
|
||||||
final handled = await urlDelegate(url);
|
await urlDelegate(Uri.parse(url));
|
||||||
if (handled) {
|
return NavigationDecision.prevent;
|
||||||
return NavigationDecision.prevent;
|
}
|
||||||
}
|
if (await launcher.canLaunch(url)) {
|
||||||
|
await launcher.launch(url);
|
||||||
}
|
}
|
||||||
await launcher.launch(url);
|
|
||||||
return NavigationDecision.prevent;
|
return NavigationDecision.prevent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,11 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>LSApplicationQueriesSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>https</string>
|
||||||
|
<string>http</string>
|
||||||
|
</array>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
|
|||||||
@@ -260,18 +260,22 @@ class ComposerController extends BaseController {
|
|||||||
|
|
||||||
if (listToEmailAddress.isNotEmpty || listCcEmailAddress.isNotEmpty || listBccEmailAddress.isNotEmpty) {
|
if (listToEmailAddress.isNotEmpty || listCcEmailAddress.isNotEmpty || listBccEmailAddress.isNotEmpty) {
|
||||||
isInitialRecipient.value = true;
|
isInitialRecipient.value = true;
|
||||||
|
toAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listCcEmailAddress.isNotEmpty) {
|
if (listCcEmailAddress.isNotEmpty) {
|
||||||
listEmailAddressType.add(PrefixEmailAddress.cc);
|
listEmailAddressType.add(PrefixEmailAddress.cc);
|
||||||
|
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listBccEmailAddress.isNotEmpty) {
|
if (listBccEmailAddress.isNotEmpty) {
|
||||||
listEmailAddressType.add(PrefixEmailAddress.bcc);
|
listEmailAddressType.add(PrefixEmailAddress.bcc);
|
||||||
|
bccAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||||
}
|
}
|
||||||
} else if (arguments.emailAddress != null) {
|
} else if (arguments.emailAddress != null) {
|
||||||
listToEmailAddress.add(arguments.emailAddress!);
|
listToEmailAddress.add(arguments.emailAddress!);
|
||||||
isInitialRecipient.value = true;
|
isInitialRecipient.value = true;
|
||||||
|
toAddressExpandMode.value = ExpandMode.COLLAPSE;
|
||||||
}
|
}
|
||||||
_updateStatusEmailSendButton();
|
_updateStatusEmailSendButton();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -484,11 +484,11 @@ class EmailController extends BaseController {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
|
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
|
||||||
builder: (BuildContext context) => (EmailAddressDialogBuilder(context, imagePaths, emailAddress)
|
builder: (BuildContext context) => PointerInterceptor(child: (EmailAddressDialogBuilder(context, imagePaths, emailAddress)
|
||||||
..addOnCloseContextMenuAction(() => popBack())
|
..addOnCloseContextMenuAction(() => popBack())
|
||||||
..addOnCopyEmailAddressAction((emailAddress) => copyEmailAddress(context, emailAddress))
|
..addOnCopyEmailAddressAction((emailAddress) => copyEmailAddress(context, emailAddress))
|
||||||
..addOnComposeEmailAction((emailAddress) => composeEmailFromEmailAddress(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) {
|
void deleteEmailPermanently(BuildContext context, PresentationEmail email) {
|
||||||
if (responsiveUtils.isMobile(context)) {
|
if (responsiveUtils.isMobile(context)) {
|
||||||
(ConfirmationDialogActionSheetBuilder(context)
|
(ConfirmationDialogActionSheetBuilder(context)
|
||||||
|
|||||||
@@ -536,12 +536,14 @@ class EmailView extends GetView with UserSettingPopupMenuMixin, NetworkConnectio
|
|||||||
widthContent: constraints.maxWidth,
|
widthContent: constraints.maxWidth,
|
||||||
heightContent: MediaQuery.of(context).size.height,
|
heightContent: MediaQuery.of(context).size.height,
|
||||||
contentHtml: allEmailContents,
|
contentHtml: allEmailContents,
|
||||||
controller: HtmlViewerControllerForWeb());
|
controller: HtmlViewerControllerForWeb(),
|
||||||
|
mailtoDelegate: (uri) => emailController.openMailToLink(uri));
|
||||||
} else {
|
} else {
|
||||||
return HtmlContentViewer(
|
return HtmlContentViewer(
|
||||||
widthContent: MediaQuery.of(context).size.width,
|
widthContent: MediaQuery.of(context).size.width,
|
||||||
contentHtml: allEmailContents,
|
contentHtml: allEmailContents,
|
||||||
loadingWidget: EmailContentPlaceHolderLoading(responsiveUtils: responsiveUtils));
|
loadingWidget: EmailContentPlaceHolderLoading(responsiveUtils: responsiveUtils),
|
||||||
|
mailtoDelegate: (uri) async => emailController.openMailToLink(uri));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return SizedBox.shrink();
|
return SizedBox.shrink();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/model.dart';
|
||||||
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
typedef OnCloseBottomSheetAction = void Function();
|
typedef OnCloseBottomSheetAction = void Function();
|
||||||
@@ -56,7 +57,7 @@ class EmailAddressBottomSheetBuilder {
|
|||||||
|
|
||||||
void show() {
|
void show() {
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
GestureDetector(
|
PointerInterceptor(child: GestureDetector(
|
||||||
onTap: () => _onCloseBottomSheetAction?.call(),
|
onTap: () => _onCloseBottomSheetAction?.call(),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -73,11 +74,11 @@ class EmailAddressBottomSheetBuilder {
|
|||||||
onPressed: () => _onCloseBottomSheetAction?.call(),
|
onPressed: () => _onCloseBottomSheetAction?.call(),
|
||||||
icon: SvgPicture.asset(_imagePaths.icCloseMailbox, width: 24, height: 24, fit: BoxFit.fill))),
|
icon: SvgPicture.asset(_imagePaths.icCloseMailbox, width: 24, height: 24, fit: BoxFit.fill))),
|
||||||
(AvatarBuilder()
|
(AvatarBuilder()
|
||||||
..text('${_emailAddress.asString().characters.first.toUpperCase()}')
|
..text('${_emailAddress.asString().characters.first.toUpperCase()}')
|
||||||
..size(64)
|
..size(64)
|
||||||
..addTextStyle(TextStyle(fontWeight: FontWeight.w600, fontSize: 23, color: Colors.white))
|
..addTextStyle(TextStyle(fontWeight: FontWeight.w600, fontSize: 23, color: Colors.white))
|
||||||
..avatarColor(_emailAddress.avatarColors))
|
..avatarColor(_emailAddress.avatarColors))
|
||||||
.build(),
|
.build(),
|
||||||
if (_emailAddress.displayName.isNotEmpty)
|
if (_emailAddress.displayName.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(left: 16, right: 16, top: 16),
|
padding: EdgeInsets.only(left: 16, right: 16, top: 16),
|
||||||
@@ -125,7 +126,7 @@ class EmailAddressBottomSheetBuilder {
|
|||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
)),
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
shape: _shape(),
|
shape: _shape(),
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user