diff --git a/app/lib/common/constants.dart b/app/lib/common/constants.dart index c1b884e0..1393f8e6 100644 --- a/app/lib/common/constants.dart +++ b/app/lib/common/constants.dart @@ -7,32 +7,6 @@ Uri labServerUrl([String slug = '']) => Uri keycloakUrl([String slug = '']) => Uri.http('vm-slosarek01.dhclab.i.hpi.de:28080', slug); -// Note that sending emails will not work on the iPhone Simulator since it does -// not have any email application installed. -String contactEmailAddress = 'ehivepgx@mssm.edu'; -// Workaround according to https://pub.dev/packages/url_launcher#encoding-urls -String? _encodeQueryParameters(Map params) { -return params.entries - .map((entry) => - '${Uri.encodeComponent(entry.key)}=${Uri.encodeComponent(entry.value)}' - ).join('&'); -} -Future sendEmail({ - String subject = '', - String body = '', -}) async { - await launchUrl( - Uri( - scheme: 'mailto', - path: contactEmailAddress, - query: _encodeQueryParameters({ - 'subject': subject, - 'body': body, - }), - ), - ); -} - final geneticInformationUrl = Uri.https( 'medlineplus.gov', '/genetics/understanding/', diff --git a/app/lib/common/utilities/contact.dart b/app/lib/common/utilities/contact.dart new file mode 100644 index 00000000..6f5d3cd5 --- /dev/null +++ b/app/lib/common/utilities/contact.dart @@ -0,0 +1,146 @@ +import 'package:flutter/services.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../module.dart'; + +// Note that sending emails will not work on the iPhone Simulator since it does +// not have any email application installed. + +String contactEmailAddress = 'ehivepgx@mssm.edu'; + +class CopyText extends HookWidget { + const CopyText({ + required this.text, + this.label, + this.bold = false, + this.scrollable = false, + }); + + final String text; + final String? label; + final bool bold; + final bool scrollable; + + @override + Widget build(BuildContext context) { + final copySuccess = useState(false); + var textStyle = TextStyle(); + if (bold) textStyle = textStyle.copyWith(fontWeight: FontWeight.bold); + if (scrollable) { + textStyle = textStyle.copyWith( + backgroundColor: PharMeTheme.onSurfaceColor, + ); + } + final textComponent = Text( + text, + style: textStyle, + maxLines: 1, + ); + final icon = copySuccess.value + ? Icons.check + : Icons.copy; + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (label.isNotNullOrBlank) ...[ + Text( + label!, + style: TextStyle(fontWeight: FontWeight.bold), + ), + SizedBox(width: PharMeTheme.smallSpace), + ], + Expanded( + child: scrollable + ? SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: textComponent, + ) + : textComponent, + ), + SizedBox(width: PharMeTheme.smallSpace), + GestureDetector( + child: Icon( + icon, + color: PharMeTheme.iconColor, + size: PharMeTheme.mediumSpace, + ), + onTap: () async => { + copySuccess.value = true, + await Clipboard.setData(ClipboardData(text: text)), + Future.delayed(Duration(seconds: 1), () => copySuccess.value = false), + }, + ), + ], + ); + } + +} + +// Workaround according to https://pub.dev/packages/url_launcher#encoding-urls +String? _encodeQueryParameters(Map params) { +return params.entries + .map((entry) => + '${Uri.encodeComponent(entry.key)}=${Uri.encodeComponent(entry.value)}' + ).join('&'); +} +Future sendEmail(BuildContext context, { + String subject = '', + String body = '', +}) async { + await showAdaptiveDialog( + context: context, + builder: (context) => DialogWrapper( + title: context.l10n.more_page_contact_us, + content: SingleChildScrollView( + child: Column( + children: [ + SizedBox(height: PharMeTheme.smallSpace), + Text(context.l10n.contact_text), + SizedBox(height: PharMeTheme.smallSpace), + CopyText(text: contactEmailAddress, bold: true), + if (subject.isNotBlank || body.isNotBlank) ...[ + SizedBox(height: PharMeTheme.smallSpace), + Text(context.l10n.contact_context_text), + if (subject.isNotBlank) ...[ + SizedBox(height: PharMeTheme.smallSpace), + CopyText( + text: subject, + label: context.l10n.contact_subject, + scrollable: true, + ), + ], + if (body.isNotBlank) ...[ + SizedBox(height: PharMeTheme.smallSpace), + CopyText( + text: body, + label: context.l10n.contact_body, + scrollable: true, + ), + ], + ], + ], + ), + ), + actions: [ + DialogAction( + text: context.l10n.action_cancel, + onPressed: () => Navigator.pop(context), + ), + DialogAction( + text: context.l10n.contact_open_mail, + isDefault: true, + onPressed: () async => launchUrl( + Uri( + scheme: 'mailto', + path: contactEmailAddress, + query: _encodeQueryParameters({ + 'subject': subject, + 'body': body, + }), + ), + ), + ), + ], + ), + ); +} \ No newline at end of file diff --git a/app/lib/common/utilities/module.dart b/app/lib/common/utilities/module.dart index 20e5ed43..a076f0a2 100644 --- a/app/lib/common/utilities/module.dart +++ b/app/lib/common/utilities/module.dart @@ -1,4 +1,5 @@ export 'color_utils.dart'; +export 'contact.dart'; export 'drug_utils.dart'; export 'genome_data.dart'; export 'material_colors.dart'; diff --git a/app/lib/common/widgets/dialog_action.dart b/app/lib/common/widgets/dialog_action.dart index 7c5155dc..87d02c3c 100644 --- a/app/lib/common/widgets/dialog_action.dart +++ b/app/lib/common/widgets/dialog_action.dart @@ -6,11 +6,13 @@ class DialogAction extends StatelessWidget { const DialogAction({ super.key, this.isDestructive = false, + this.isDefault = false, this.onPressed, required this.text, }); final bool isDestructive; + final bool isDefault; final void Function()? onPressed; final String text; @@ -20,6 +22,7 @@ class DialogAction extends StatelessWidget { case SupportedPlatform.ios: return CupertinoDialogAction( isDestructiveAction: isDestructive, + isDefaultAction: isDefault, onPressed: onPressed, child: Text(text), ); diff --git a/app/lib/error/pages/error.dart b/app/lib/error/pages/error.dart index 44cb5396..0232374d 100644 --- a/app/lib/error/pages/error.dart +++ b/app/lib/error/pages/error.dart @@ -43,6 +43,7 @@ class ErrorPage extends StatelessWidget { text: contactEmailAddress, style: PharMeTheme.textTheme.bodyLarge, onTap: () => sendEmail( + context, subject: context.l10n.error_mail_subject, body: context.l10n.error_mail_body(error), ), diff --git a/app/lib/faq/pages/faq.dart b/app/lib/faq/pages/faq.dart index 1ce2e2f4..a13c6871 100644 --- a/app/lib/faq/pages/faq.dart +++ b/app/lib/faq/pages/faq.dart @@ -30,10 +30,9 @@ class FaqPage extends StatelessWidget { title: Text(context.l10n.faq_contact_us), trailing: Icon(Icons.chevron_right_rounded), iconColor: PharMeTheme.iconColor, - onTap: sendEmail + onTap: () => sendEmail(context), ) - ) - + ), ], ), ), diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index eadeb10b..c29c1ce2 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -396,6 +396,12 @@ "more_page_genetic_information": "Learn about genetics (MedlinePlus)", "more_page_contact_us": "Contact us", + "contact_text": "You can contact us using the following email address:", + "contact_context_text": "To help us to understand the context of your message, please include the following information:", + "contact_subject": "Subject:", + "contact_body": "Text:", + "contact_open_mail": "Send mail", + "comprehension_intro_text": "Would you like to participate in a survey aiming to measure user comprehension of content in the app? This would help us make PharMe more understandable for everyone!", "comprehension_survey_button_text": "Continue to survey" } diff --git a/app/lib/more/pages/more.dart b/app/lib/more/pages/more.dart index 926dd780..75698019 100644 --- a/app/lib/more/pages/more.dart +++ b/app/lib/more/pages/more.dart @@ -67,7 +67,7 @@ class MorePage extends StatelessWidget { onTap: openFurtherGeneticInformation), _buildSettingsItem( title: context.l10n.more_page_contact_us, - onTap: sendEmail) + onTap: () => sendEmail(context)), ] ), );