diff --git a/assets/translations/en.json b/assets/translations/en.json index da91a15f1bdd56fb6dbefc4513f87320dd862279..8b7720f1555dd5b1e7274ca8de978c3b916b4f67 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -196,10 +196,6 @@ "payment_waiting_internet_desc_beta": "Retry your payment when internet is back", "your_name_here": "Name this card", "copied_to_clipboard": "Copied to clipboard", - "share_export_title": "Share your wallet", - "share_export_desc": "Your wallet has been exported locally. Would you like to additionally share it with yourself via email/chat/etc. for safekeeping?", - "share_export_subject": "My Äž1nkgo Wallet", - "share_export_button": "SHARE", "pay_with_nfc_tooltip": "To receive a payment, simply bring this device close to the other wallet with NFC activated.", "import_wallet_from_clipboard": "Import Wallet from Clipboard", "import_wallet_from_clipboard_desc": "Please paste a previously exported wallet text to import it", @@ -207,12 +203,14 @@ "paste": "PASTE", "import": "IMPORT", "select_import_method": "Select Import Method", - "select_import_method_desc": "Please choose where to import the wallet from", "file_import": "Import from File", "export": "EXPORT", "clipboard_export": "Export to Clipboard", "select_export_method": "Select Export Method", - "select_export_method_desc": "Please choose where to export the wallet", "file_export": "Export to File", - "clipboard_export": "Export to Clipboard" + "clipboard_export": "Export to Clipboard", + "share_export": "Share via email/chat/etc.", + "wallet_copied": "Walled copied to clipboard", + "link_export": "Export to Link", + "share_export_subject": "Äž1nkgo Wallet Export" } diff --git a/assets/translations/es.json b/assets/translations/es.json index b9b47d977cfcc502007d18876ae258c4d4540076..d7837843437f86eecceda650e9828e168fc0e0eb 100644 --- a/assets/translations/es.json +++ b/assets/translations/es.json @@ -198,10 +198,6 @@ "payment_waiting_internet_desc_beta": "Reintenta el pago cuando vuelvas a tener conexión", "your_name_here": "Da un nombre a esta tarjeta", "copied_to_clipboard": "Copiado al portapapeles", - "share_export_title": "Comporte tu monedero", - "share_export_desc": "Tu monedero ha sido exportado localmente. ¿Te gustarÃa compartirlo adicionalmente contigo mismo vÃa email/chat/etc. para su resguardo?", - "share_export_subject": "Mi monedero Äž1nkgo", - "share_export_button": "COMPARTIR", "pay_with_nfc_tooltip": "Para recibir un pago, simplemente acerca este dispositivo al otro monedero con NFC activado.", "import_wallet_from_clipboard": "Importar monedero desde el portapapeles", "import_wallet_from_clipboard_desc": "Por favor, pega aquà el textod del backup previo de tu monedero", @@ -209,13 +205,15 @@ "paste": "PEGAR", "import": "IMPORTAR", "select_import_method": "Selecciona el método de importación", - "select_import_method_desc": "Por favor, selecciona el método de importación de tu monedero", "file_import": "Importar desde archivo", "clipboard_import": "Importar desde portapapeles", "export": "EXPORTAR", "clipboard_export": "Exportar al portapapeles", "select_export_method": "Selecciona el método de exportación", - "select_export_method_desc": "Por favor, selecciona el método de exportación de tu monedero", - "file_export": "Exportar a archivo", - "clipboard_export": "Exportar al portapapeles" + "file_export": "Exportar a un archivo", + "clipboard_export": "Exportar al portapapeles", + "share_export": "Exportar vÃa email/chat/etc.", + "wallet_copied": "Monedero copiado al portapapeles", + "link_export": "Exportar vÃa enlace", + "share_export_subject": "Äž1nkgo Wallet Export" } diff --git a/lib/ui/screens/fifth_screen.dart b/lib/ui/screens/fifth_screen.dart index 5ccd2aa44a2bc931e93f743668fae9edfe45ab0b..9e64c1777e32f2cd970d809dcaaaeb1e2a22a838 100644 --- a/lib/ui/screens/fifth_screen.dart +++ b/lib/ui/screens/fifth_screen.dart @@ -9,7 +9,6 @@ import '../../data/models/app_state.dart'; import '../../data/models/bottom_nav_cubit.dart'; import '../../data/models/theme_cubit.dart'; import '../../shared_prefs.dart'; -import '../notification_controller.dart'; import '../tutorial.dart'; import '../tutorial_keys.dart'; import '../ui_helpers.dart'; @@ -82,7 +81,7 @@ class _FifthScreenState extends State<FifthScreen> { ), onChanged: (Locale? newLocale) { context.setLocale(newLocale!); - NotificationController.locale = newLocale; + // NotificationController.locale = newLocale; }, items: const <DropdownMenuItem<Locale>>[ DropdownMenuItem<Locale>( @@ -180,12 +179,7 @@ class _FifthScreenState extends State<FifthScreen> { title: 'export_key', icon: Icons.download, onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return const ExportDialog(); - }, - ); + _showSelectExportMethodDialog(); }), GridItem( title: 'import_key', @@ -260,6 +254,24 @@ class _FifthScreenState extends State<FifthScreen> { ); } } + + Future<void> _showSelectExportMethodDialog() async { + final ExportType? method = await showDialog<ExportType>( + context: context, + builder: (BuildContext context) => const SelectExportMethodDialog(), + ); + if (method != null) { + if (!mounted) { + return; + } + showDialog( + context: context, + builder: (BuildContext context) { + return ExportDialog(type: method); + }, + ); + } + } } class SelectImportMethodDialog extends StatelessWidget { @@ -269,15 +281,43 @@ class SelectImportMethodDialog extends StatelessWidget { Widget build(BuildContext context) { return AlertDialog( title: Text(tr('select_import_method')), - content: Text(tr('select_import_method_desc')), + // content: Text(tr('select_import_method_desc')), actions: <Widget>[ - TextButton( - child: Text(tr('file_import')), + TextButton.icon( + icon: const Icon(Icons.file_present), + label: Text(tr('file_import')), onPressed: () => Navigator.of(context).pop('file')), - TextButton( - child: Text(tr('clipboard_import')), + TextButton.icon( + icon: const Icon(Icons.content_paste), + label: Text(tr('clipboard_import')), onPressed: () => Navigator.of(context).pop('clipboard')), ], ); } } + +class SelectExportMethodDialog extends StatelessWidget { + const SelectExportMethodDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(tr('select_export_method')), + // content: Text(tr('select_export_method_desc')), + actions: <Widget>[ + TextButton.icon( + icon: const Icon(Icons.file_present), + label: Text(tr('file_export')), + onPressed: () => Navigator.of(context).pop(ExportType.file)), + TextButton.icon( + icon: const Icon(Icons.content_paste), + label: Text(tr('clipboard_export')), + onPressed: () => Navigator.of(context).pop(ExportType.clipboard)), + TextButton.icon( + icon: const Icon(Icons.share), + label: Text(tr('share_export')), + onPressed: () => Navigator.of(context).pop(ExportType.share)), + ], + ); + } +} diff --git a/lib/ui/widgets/fifth_screen/export_dialog.dart b/lib/ui/widgets/fifth_screen/export_dialog.dart index fd55b553c305b26cc96425a11926ab3094c7efa7..9e90edfb5a85e9ce66bbc4d77cb2b4c7214f1c8f 100644 --- a/lib/ui/widgets/fifth_screen/export_dialog.dart +++ b/lib/ui/widgets/fifth_screen/export_dialog.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:clipboard/clipboard.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:file_saver/file_saver.dart'; import 'package:flutter/foundation.dart'; @@ -19,8 +20,12 @@ import '../../logger.dart'; import '../../ui_helpers.dart'; import 'pattern_util.dart'; +enum ExportType { clipboard, file, share } + class ExportDialog extends StatefulWidget { - const ExportDialog({super.key}); + const ExportDialog({super.key, required this.type}); + + final ExportType type; @override State<ExportDialog> createState() => _ExportDialogState(); @@ -66,7 +71,7 @@ class _ExportDialogState extends State<ExportDialog> { if (isConfirm) { if (listEquals<int>(input, pattern)) { Navigator.of(context).pop(); - _export(pattern!.join(), context); + _export(pattern!.join(), context, widget.type); } else { context.replaceSnackbar( content: Text( @@ -93,70 +98,67 @@ class _ExportDialogState extends State<ExportDialog> { ); } - Future<void> _export(String password, BuildContext context) async { + Future<void> _export(String password, BuildContext context, + ExportType type) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); - final String jsonString = jsonEncode(prefs .getKeys() .fold<Map<String, dynamic>>( - <String, dynamic>{}, + <String, dynamic>{}, (Map<String, dynamic> map, String key) => - <String, dynamic>{...map, key: prefs.get(key)})); + <String, dynamic>{...map, key: prefs.get(key)})); final Map<String, String> jsonData = - encryptJsonForExport(jsonString, password); + encryptJsonForExport(jsonString, password); final String fileJson = jsonEncode(jsonData); final List<int> bytes = utf8.encode(fileJson); - if (kIsWeb) { - webDownload(bytes); - } else { - saveFile(bytes); - } - - if (!mounted) { - return; + switch (type) { + case ExportType.clipboard: + FlutterClipboard.copy(fileJson) + .then((dynamic value) => + context.replaceSnackbar( + content: Text( + tr('wallet_copied'), + style: const TextStyle(color: Colors.red), + ), + )); + break; + case ExportType.file: + if (kIsWeb) { + webDownload(bytes); + } else { + saveFile(bytes); + } + if (!mounted) { + return; + } + context.replaceSnackbar( + content: Text( + tr('wallet_exported'), + style: const TextStyle(color: Colors.red), + ), + ); + break; + case ExportType.share: + if (!mounted) { + return; + } + shareExport(context, fileJson); + break; } - context.replaceSnackbar( - content: Text( - tr('wallet_exported'), - style: const TextStyle(color: Colors.red), - ), - ); - confirmAndShare(context, fileJson); } - void confirmAndShare(BuildContext context, String fileJson) { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text(tr('share_export_title')), - content: Text(tr('share_export_desc')), - actions: <Widget>[ - TextButton( - child: Text(tr('cancel')), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: Text(tr('share_export_button')), - onPressed: () { - if (kIsWeb) { - /* final String url = - 'mailto:?subject=${Uri.encodeComponent('My wallet')}&body=${Uri.encodeComponent(fileJson)}'; - html.window.open(url, '_blank'); */ - Share.share(fileJson, subject: tr('share_export_subject')); - } else { - Share.share(fileJson, subject: tr('share_export_subject')); - } - Navigator.of(context).pop(); - }, - ), - ], - ); - }, - ); + Future<void> shareExport(BuildContext context, String fileJson) { + if (kIsWeb) { + final Uri uri = Uri.parse(html.window.location.href); + final String fileJsonUrlComponent = Uri.encodeComponent(fileJson); + final Uri finalUri = uri.replace(path: '/import/$fileJsonUrlComponent'); + // TODO Allow to import this link + return Share.share(inDevelopment ? finalUri.toString() : fileJson, + subject: tr('share_export_subject')); + } else { + return Share.share(fileJson, subject: tr('share_export_subject')); + } } void webDownload(List<int> bytes) { @@ -165,14 +167,15 @@ class _ExportDialogState extends State<ExportDialog> { final html.AnchorElement anchor = html.AnchorElement(href: url); anchor.download = - 'ginkgo-wallet-${simplifyPubKey(SharedPreferencesHelper().getPubKey())}.json'; + 'ginkgo-wallet-${simplifyPubKey( + SharedPreferencesHelper().getPubKey())}.json'; anchor.click(); } Future<void> saveFile(List<int> bytes) async { try { final Directory? externalDirectory = - await getAppSpecificExternalFilesDirectory(); // ensureDownloadsDirectoryExists(); + await getAppSpecificExternalFilesDirectory(); // ensureDownloadsDirectoryExists(); if (externalDirectory == null) { logger('Downloads directory not found'); return; @@ -203,7 +206,8 @@ class _ExportDialogState extends State<ExportDialog> { String walletFileName() { final String fileName = - 'ginkgo-wallet-${simplifyPubKey(SharedPreferencesHelper().getPubKey())}.json'; + 'ginkgo-wallet-${simplifyPubKey( + SharedPreferencesHelper().getPubKey())}.json'; return fileName; } }