diff --git a/lib/core/csv_export_service.dart b/lib/core/csv_export_service.dart new file mode 100644 index 000000000..ae6fa347a --- /dev/null +++ b/lib/core/csv_export_service.dart @@ -0,0 +1,317 @@ +import 'dart:io'; + +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/backup/backup_page.dart'; +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/utils/share_util.dart'; +import 'package:cake_wallet/utils/show_bar.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/view_model/dashboard/action_list_item.dart'; +import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart'; +import 'package:cake_wallet/view_model/dashboard/date_section_item.dart'; +import 'package:cake_wallet/view_model/dashboard/order_list_item.dart'; +import 'package:cake_wallet/view_model/dashboard/payjoin_transaction_list_item.dart'; +import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart'; +import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart'; +import 'package:cw_core/transaction_direction.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:path_provider/path_provider.dart'; + +class CsvExportService { + static const _columns = [ + 'record_type', + 'date_time', + 'type', + 'amount', + 'currency', + 'fee', + 'tx_id', + 'address', + 'status', + 'note', + 'from_amount', + 'from_currency', + 'to_amount', + 'to_currency', + 'trade_id', + 'provider', + 'confirmations', + ]; + + static const _utf8Bom = ''; + + String buildCsvContent(List items) { + final buf = StringBuffer(); + buf.write(_utf8Bom); + buf.writeln(_columns.join(',')); + + for (final item in items) { + if (item is DateSectionItem) continue; + + final row = _buildRow(item); + if (row != null) buf.writeln(row); + } + + return buf.toString(); + } + + String? _buildRow(ActionListItem item) { + if (item is TransactionListItem) return _transactionRow(item); + if (item is TradeListItem) return _tradeRow(item); + if (item is OrderListItem) return _orderRow(item); + if (item is AnonpayTransactionListItem) return _anonpayRow(item); + if (item is PayjoinTransactionListItem) return _payjoinRow(item); + return null; + } + + String _transactionRow(TransactionListItem item) { + final tx = item.transaction; + final type = tx.direction == TransactionDirection.incoming ? 'incoming' : 'outgoing'; + final status = tx.isPending ? 'pending' : 'confirmed'; + + // Prefer tx.to/tx.from; fall back to address lists for chains that don't populate them. + final address = tx.direction == TransactionDirection.incoming + ? (tx.from?.isNotEmpty == true + ? tx.from! + : (tx.inputAddresses?.firstOrNull ?? '')) + : (tx.to?.isNotEmpty == true + ? tx.to! + : (tx.outputAddresses?.firstOrNull ?? '')); + + // Split "0.2769 LTC" → amount="0.2769", currency="LTC" + final (amount, currency) = _splitAmountCurrency(tx.amountFormatted()); + + // fee is stored as (inputAmount - totalOutputAmount) which is negative for incoming + // transactions — only include it when the raw value is a sensible positive fee. + final fee = tx.fee != null && tx.fee! > 0 ? (tx.feeFormatted() ?? '') : ''; + final (feeAmount, _) = fee.isNotEmpty ? _splitAmountCurrency(fee) : ('', ''); + + return _row([ + 'transaction', + _isoDate(tx.date), + type, + amount, + currency, + feeAmount, + tx.id, + address, + status, + '', + '', + '', + '', + '', + '', + '', + tx.confirmations.toString(), + ]); + } + + /// Splits a formatted amount like "0.2769 LTC" into ("0.2769", "LTC"). + /// If there is no trailing symbol the whole string is returned as the amount. + (String, String) _splitAmountCurrency(String formatted) { + final trimmed = formatted.trim(); + final idx = trimmed.lastIndexOf(' '); + if (idx == -1) return (trimmed, ''); + return (trimmed.substring(0, idx), trimmed.substring(idx + 1)); + } + + String _tradeRow(TradeListItem item) { + final trade = item.trade; + return _row([ + 'trade', + _isoDate(trade.createdAt ?? DateTime.fromMillisecondsSinceEpoch(0, isUtc: true)), + 'swap', + '', + '', + trade.fee?.toString() ?? '', + trade.outputTransaction ?? '', + trade.payoutAddress ?? '', + trade.state.title, + trade.memo ?? '', + trade.amount, + trade.from?.name ?? '', + trade.receiveAmount ?? '', + trade.to?.name ?? '', + trade.id, + trade.provider.title, + '', + ]); + } + + String _orderRow(OrderListItem item) { + final order = item.order; + return _row([ + 'order', + _isoDate(order.createdAt), + order.source.title, + order.amountFormatted(), + order.from ?? '', + '', + order.transferId, + '', + order.state.title, + '', + order.amountFormatted(), + order.from ?? '', + order.receiveAmount ?? '', + order.to ?? '', + order.id, + order.providerTitle, + '', + ]); + } + + String _anonpayRow(AnonpayTransactionListItem item) { + final tx = item.transaction; + final amount = tx.fiatAmount?.toString() ?? tx.amountTo?.toString() ?? ''; + final currency = tx.fiatEquiv ?? tx.coinTo; + + return _row([ + 'anonpay', + _isoDate(tx.createdAt), + 'anonymous_payment', + amount, + currency, + '', + tx.invoiceId, + tx.address, + tx.status, + '', + '', + '', + '', + '', + '', + tx.provider, + '', + ]); + } + + String _payjoinRow(PayjoinTransactionListItem item) { + final session = item.session; + final type = session.isSenderSession ? 'send' : 'receive'; + final amount = session.rawAmount ?? '0'; + + return _row([ + 'payjoin', + _isoDate(session.inProgressSince ?? DateTime.fromMillisecondsSinceEpoch(0, isUtc: true)), + type, + amount, + 'BTC', + '', + session.txId ?? '', + '', + item.status, + '', + '', + '', + '', + '', + '', + '', + '', + ]); + } + + String _row(List fields) => fields.map(escapeField).join(','); + + String escapeField(String field) { + if (field.contains(',') || field.contains('"') || field.contains('\n')) { + return '"${field.replaceAll('"', '""')}"'; + } + return field; + } + + String _isoDate(DateTime dt) => dt.toUtc().toIso8601String(); + + Future exportToCsv(List items, BuildContext context) async { + final dataItems = items.whereType().where((e) => e is! DateSectionItem).toList(); + + if (dataItems.isEmpty) { + await showBar(context, S.current.csv_nothing_to_export); + return; + } + + final now = DateTime.now(); + final fileName = 'cake_wallet_export_${DateFormat('yyyyMMdd_HHmmss').format(now)}.csv'; + + late File csvFile; + + final exportFuture = Future(() async { + final content = buildCsvContent(items); + csvFile = await _writeTempFile(fileName, content); + }); + + showPersistentActionOverlay(context, exportFuture, text: S.current.generating_csv); + await exportFuture; + + if (!context.mounted) return; + + if (Platform.isAndroid) { + _showAndroidExportDialog(context, csvFile, fileName); + } else if (Platform.isIOS) { + await _shareFile(csvFile, fileName, context); + } else { + await _saveFileDesktop(csvFile, fileName); + } + } + + void _showAndroidExportDialog(BuildContext context, File csvFile, String fileName) { + showPopUp( + context: context, + builder: (dialogContext) { + return AlertWithTwoActions( + alertTitle: S.current.export_csv, + alertContent: S.current.select_destination, + rightButtonText: S.current.save_to_downloads, + leftButtonText: S.current.share, + actionRightButton: () async { + await _saveToDownloads(fileName, csvFile); + Navigator.of(dialogContext).pop(); + await showBar(context, S.current.file_saved); + await csvFile.delete(); + }, + actionLeftButton: () async { + Navigator.of(dialogContext).pop(); + await _shareFile(csvFile, fileName, context); + }, + ); + }, + ); + } + + Future _shareFile(File file, String fileName, BuildContext context) async { + await ShareUtil.shareFile(filePath: file.path, fileName: fileName, context: context); + if (await file.exists()) await file.delete(); + } + + Future _saveFileDesktop(File csvFile, String fileName) async { + final outputPath = await FilePicker.platform.saveFile( + dialogTitle: 'Save CSV export', + fileName: fileName, + lockParentWindow: true, + ); + if (outputPath == null) return; + await csvFile.copy(outputPath); + await csvFile.delete(); + } + + Future _writeTempFile(String fileName, String content) async { + final dir = await getApplicationDocumentsDirectory(); + final file = File('${dir.path}/$fileName'); + if (file.existsSync()) file.deleteSync(); + await file.writeAsString(content, flush: true); + return file; + } + + Future _saveToDownloads(String fileName, File file) async { + if (!Platform.isAndroid) return; + const downloadsPath = '/storage/emulated/0/Download'; + final dest = File('$downloadsPath/$fileName'); + if (dest.existsSync()) dest.deleteSync(); + await file.copy(dest.path); + } +} diff --git a/lib/new-ui/pages/send_page.dart b/lib/new-ui/pages/send_page.dart index 1a77db067..1fd28fc9b 100644 --- a/lib/new-ui/pages/send_page.dart +++ b/lib/new-ui/pages/send_page.dart @@ -763,7 +763,7 @@ class _NewSendPageState extends State { conditionToDetermineIfToUse2FA: check, onAuthSuccess: (value) async { if (value) { - if (!widget.mode.popOnConfirmation) { + if (!widget.mode.popOnConfirmation && Navigator.canPop(context)) { Navigator.of(context, rootNavigator: true).pop(); } showModalBottomSheet( diff --git a/lib/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart b/lib/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart index 9ea80989d..3b5d1fde1 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/core/csv_export_service.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/new-ui/widgets/line_tab_switcher.dart'; import 'package:cake_wallet/routes.dart'; @@ -24,6 +25,9 @@ class AssetsTopBar extends StatelessWidget { Widget build(BuildContext context) { final settingsButtonText = _getSettingsButtonText(); final hasTokenSettingsButton = settingsButtonText != null; + // Reuse the exact same detection logic as _getSettingsButtonText so the + // export button always matches whether the Filters button is visible. + final isHistoryTab = settingsButtonText == S.current.filters; return SliverToBoxAdapter( child: Padding( @@ -39,8 +43,33 @@ class AssetsTopBar extends StatelessWidget { selectedTab: selectedTab, ) else SizedBox.shrink(), - Opacity( - opacity: hasTokenSettingsButton ? 1 : 0, + Row( + spacing: 8, + children: [ + if (isHistoryTab) + GestureDetector( + onTap: () => + CsvExportService().exportToCsv(dashboardViewModel.items, context), + child: Semantics( + label: S.of(context).export_csv, + button: true, + child: Container( + height: 36, + width: 36, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context).colorScheme.surfaceContainer, + ), + child: Icon( + Icons.file_download_outlined, + color: Theme.of(context).colorScheme.onSurface, + size: 20, + ), + ), + ), + ), + Opacity( + opacity: hasTokenSettingsButton ? 1 : 0, child: ElevatedButton( onPressed: () { if (tabs[selectedTab] == S.of(context).assets) { @@ -48,40 +77,41 @@ class AssetsTopBar extends StatelessWidget { Routes.homeSettings, arguments: dashboardViewModel.balanceViewModel, ); - } else if(tabs[selectedTab] == S.of(context).history) { + } else if (tabs[selectedTab] == S.of(context).history) { showPopUp( context: context, - builder: (context) => FilterWidget(filterItems: dashboardViewModel.filterItems), + builder: (context) => + FilterWidget(filterItems: dashboardViewModel.filterItems), ); } }, style: ElevatedButton.styleFrom( padding: EdgeInsets.zero, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(999999), - ), - backgroundColor: Theme.of( - context, - ).colorScheme.surfaceContainer, - ), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: Row( - spacing: 6, - children: [ + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(999999), + ), + backgroundColor: Theme.of(context).colorScheme.surfaceContainer, + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: Row( + spacing: 6, + children: [ CakeImageWidget( imageUrl: "assets/new-ui/options_slider.svg", colorFilter: ColorFilter.mode( Theme.of(context).colorScheme.primary, BlendMode.srcIn)), Text( - settingsButtonText??"", - style: TextStyle(color: Theme.of(context).colorScheme.primary), - ) - ], - ), - ), + settingsButtonText ?? "", + style: TextStyle(color: Theme.of(context).colorScheme.primary), + ) + ], ), + ), ), + ), + ], + ), ], ), ), diff --git a/lib/src/screens/dashboard/pages/transactions_page.dart b/lib/src/screens/dashboard/pages/transactions_page.dart index 39b0dcc81..87b67f289 100644 --- a/lib/src/screens/dashboard/pages/transactions_page.dart +++ b/lib/src/screens/dashboard/pages/transactions_page.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/core/csv_export_service.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/order/order_source_description.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/anonpay_transaction_row.dart'; @@ -65,6 +66,8 @@ class TransactionsPage extends StatelessWidget { }), HeaderRow( dashboardViewModel: dashboardViewModel, + onExportCsv: () => + CsvExportService().exportToCsv(dashboardViewModel.items, context), key: ValueKey('transactions_page_header_row_key'), ), Expanded( diff --git a/lib/src/screens/dashboard/widgets/header_row.dart b/lib/src/screens/dashboard/widgets/header_row.dart index f85a2cf6f..5b89c0e8f 100644 --- a/lib/src/screens/dashboard/widgets/header_row.dart +++ b/lib/src/screens/dashboard/widgets/header_row.dart @@ -5,9 +5,10 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; class HeaderRow extends StatelessWidget { - HeaderRow({required this.dashboardViewModel, super.key}); + HeaderRow({required this.dashboardViewModel, this.onExportCsv, super.key}); final DashboardViewModel dashboardViewModel; + final VoidCallback? onExportCsv; @override Widget build(BuildContext context) { @@ -28,31 +29,62 @@ class HeaderRow extends StatelessWidget { fontWeight: FontWeight.w500, color: Theme.of(context).colorScheme.onSurface), ), - Semantics( - container: true, - child: GestureDetector( - key: ValueKey('transactions_page_header_row_transaction_filter_button_key'), - onTap: () { - showPopUp( - context: context, - builder: (context) => FilterWidget(filterItems: dashboardViewModel.filterItems), - ); - }, - child: Semantics( - label: 'Transaction Filter', - button: true, - enabled: true, - child: Container( - height: 36, - width: 36, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.surfaceContainer, + Row( + children: [ + if (onExportCsv != null) + Padding( + padding: const EdgeInsets.only(right: 8), + child: GestureDetector( + key: ValueKey('transactions_page_header_row_export_csv_button_key'), + onTap: onExportCsv, + child: Semantics( + label: S.of(context).export_csv, + button: true, + enabled: true, + child: Container( + height: 36, + width: 36, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context).colorScheme.surfaceContainer, + ), + child: Icon( + Icons.file_download_outlined, + color: Theme.of(context).colorScheme.onSurface, + size: 20, + ), + ), + ), + ), + ), + Semantics( + container: true, + child: GestureDetector( + key: ValueKey('transactions_page_header_row_transaction_filter_button_key'), + onTap: () { + showPopUp( + context: context, + builder: (context) => + FilterWidget(filterItems: dashboardViewModel.filterItems), + ); + }, + child: Semantics( + label: 'Transaction Filter', + button: true, + enabled: true, + child: Container( + height: 36, + width: 36, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context).colorScheme.surfaceContainer, + ), + child: filterIcon, + ), ), - child: filterIcon, ), ), - ), + ], ) ], ), diff --git a/lib/utils/share_util.dart b/lib/utils/share_util.dart index cfb9e74ab..9fd2c5ebc 100644 --- a/lib/utils/share_util.dart +++ b/lib/utils/share_util.dart @@ -33,8 +33,20 @@ class ShareUtil { return Rect.zero; } - final box = context.findRenderObject() as RenderBox?; + final renderObject = context.findRenderObject(); + if (renderObject is! RenderBox) { + return Rect.zero; + } - return box!.localToGlobal(Offset.zero) & box.size; + final rect = renderObject.localToGlobal(Offset.zero) & renderObject.size; + // iOS requires sharePositionOrigin to lie fully within the source view's + // bounds; clip to the screen so an offset/oversized page rect doesn't spill + // past the edge and trigger a PlatformException on iPad. + final screen = Offset.zero & MediaQuery.of(context).size; + final clipped = rect.intersect(screen); + if (clipped.isEmpty) { + return Rect.zero; + } + return clipped; } } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 2860f09e4..8077e97f4 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -453,11 +453,14 @@ "expires": "ينتهي", "expiresOn": "تنتهي في", "expiry_and_validity": "تاريخ الانتهاء والصلاحية", + "csv_nothing_to_export": "Nothing to export", "export_backup": "تصدير النسخة الاحتياطية", + "export_csv": "Export CSV", "export_lightning_logs": "تصدير سجلات Lightning", "export_logs": "تصدير السجلات", "export_outputs": "تصدير المخرجات", "export_payjoin_logs": "تصدير سجلات Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "تمديد الجلسة", "external": "خارجي", "external_wallet": "محفظة خارجية", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 584e84963..9fb31cff8 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -453,11 +453,14 @@ "expires": "Изтича", "expiresOn": "Изтича на", "expiry_and_validity": "Срок на валидност и валидност", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Експортиране на резервно копие", + "export_csv": "Export CSV", "export_lightning_logs": "Експортиране на Lightning логове", "export_logs": "Експортиране на логове", "export_outputs": "Експортиране на изходи", "export_payjoin_logs": "Експортиране на Payjoin логове", + "generating_csv": "Generating CSV...", "extend_session": "Удължи сесията", "external": "Външен", "external_wallet": "Външен портфейл", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 65d11438d..b8eaafbc9 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -453,11 +453,14 @@ "expires": "Vyprší", "expiresOn": "Vyprší dne", "expiry_and_validity": "Vypršení platnosti", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Exportovat zálohu", + "export_csv": "Export CSV", "export_lightning_logs": "Exportovat protokoly Lightning", "export_logs": "Exportovat protokoly", "export_outputs": "Exportovat výstupy", "export_payjoin_logs": "Exportovat protokoly Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Prodloužit relaci", "external": "Externí", "external_wallet": "Externí peněženka", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 9857550a4..cf20f6db4 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -453,11 +453,14 @@ "expires": "Läuft ab", "expiresOn": "Läuft ab am", "expiry_and_validity": "Ablauf und Gültigkeit", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Backup exportieren", + "export_csv": "Export CSV", "export_lightning_logs": "Lightning-Logs exportieren", "export_logs": "Protokolle exportieren", "export_outputs": "Outputs exportieren", "export_payjoin_logs": "Payjoin-Logs exportieren", + "generating_csv": "Generating CSV...", "extend_session": "Sitzung verlängern", "external": "Extern", "external_wallet": "Externe Wallet", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 1ded5a8e7..a4a497972 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -455,11 +455,14 @@ "expires": "Expires", "expiresOn": "Expires on", "expiry_and_validity": "Expiry and Validity", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Export backup", + "export_csv": "Export CSV", "export_lightning_logs": "Export Lightning logs", "export_logs": "Export logs", "export_outputs": "Export outputs", "export_payjoin_logs": "Export Payjoin logs", + "generating_csv": "Generating CSV...", "extend_session": "Extend Session", "external": "External", "external_wallet": "External wallet", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index de1489eaf..9c9fe3d81 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -453,11 +453,14 @@ "expires": "Caduca", "expiresOn": "Caduca el", "expiry_and_validity": "Caducidad y validez", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Exportar copia de seguridad", + "export_csv": "Export CSV", "export_lightning_logs": "Exportar registros de Lightning", "export_logs": "Exportar registros", "export_outputs": "Exportar salidas", "export_payjoin_logs": "Exportar registros de Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Extender sesión", "external": "Externo", "external_wallet": "Billetera externa", diff --git a/res/values/strings_fa.arb b/res/values/strings_fa.arb index 6fcdc663c..167c4fa57 100644 --- a/res/values/strings_fa.arb +++ b/res/values/strings_fa.arb @@ -451,11 +451,14 @@ "expires": "منقضی می‌شود", "expiresOn": "انقضا در", "expiry_and_validity": "انقضا و اعتبار", + "csv_nothing_to_export": "Nothing to export", "export_backup": "صدور نسخه پشتیبان", + "export_csv": "Export CSV", "export_lightning_logs": "خروجی گرفتن از گزارش‌های لایتنینگ", "export_logs": "خروجی گرفتن از گزارش‌ها", "export_outputs": "برون‌برد خروجی‌ها", "export_payjoin_logs": "گزارش‌های Payjoin را صادر کنید", + "generating_csv": "Generating CSV...", "extend_session": "تمدید جلسه", "external": "خارجی", "external_wallet": "کیف پول خارجی", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index dc91f0e52..7fb51b38d 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -453,11 +453,14 @@ "expires": "Expire", "expiresOn": "Expire le", "expiry_and_validity": "Expiration et validité", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Exporter la sauvegarde", + "export_csv": "Export CSV", "export_lightning_logs": "Exporter les logs Lightning", "export_logs": "Exporter les journaux", "export_outputs": "Exporter les sorties", "export_payjoin_logs": "Exporter les journaux de Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Prolonger la session", "external": "Externe", "external_wallet": "Portefeuille externe", diff --git a/res/values/strings_gn.arb b/res/values/strings_gn.arb index abf322a15..e17fd2748 100644 --- a/res/values/strings_gn.arb +++ b/res/values/strings_gn.arb @@ -355,9 +355,12 @@ "expires": "Opa hag̃ua", "expiresOn": "Opa hag̃ua", "expiry_and_validity": "Opaha ára ha hekorã", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Emondo hag̃ua backup", + "export_csv": "Export CSV", "export_lightning_logs": "Emondo Lightning rembiapokue", "export_payjoin_logs": "Emondo okápe Payjoin registro-kuéra", + "generating_csv": "Generating CSV...", "external": "Okápegua", "external_wallet": "Billetera externa", "extra_id": "ID extra:", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index c36af10b8..c0a4d5150 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -453,11 +453,14 @@ "expires": "Yana ƙarewa", "expiresOn": "Yana ƙarewa a", "expiry_and_validity": "Ranar ƙarewa da inganci", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Fitar da ajiyar baya", + "export_csv": "Export CSV", "export_lightning_logs": "Fitar da log ɗin Lightning", "export_logs": "Fitar da log", "export_outputs": "Fitar da abubuwan fitarwa", "export_payjoin_logs": "Fitar da log ɗin Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Tsawaita Zama", "external": "Na waje", "external_wallet": "Wallet na waje", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index d7493f086..a1ce0c33b 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -453,11 +453,14 @@ "expires": "समाप्त होता है", "expiresOn": "इस दिन समाप्त होता है", "expiry_and_validity": "समाप्ति और वैधता", + "csv_nothing_to_export": "Nothing to export", "export_backup": "बैकअप निर्यात करें", + "export_csv": "Export CSV", "export_lightning_logs": "लाइटनिंग लॉग्स निर्यात करें", "export_logs": "लॉग निर्यात करें", "export_outputs": "आउटपुट निर्यात करें", "export_payjoin_logs": "Payjoin लॉग एक्सपोर्ट करें", + "generating_csv": "Generating CSV...", "extend_session": "सत्र बढ़ाएँ", "external": "बाहरी", "external_wallet": "बाहरी वॉलेट", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 615ca219b..a8e281b3f 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -453,11 +453,14 @@ "expires": "Ističe", "expiresOn": "Istječe", "expiry_and_validity": "Istek i valjanost", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Izvezi sigurnosnu kopiju", + "export_csv": "Export CSV", "export_lightning_logs": "Izvezi Lightning zapise", "export_logs": "Izvezi zapise", "export_outputs": "Izvoz izlaza", "export_payjoin_logs": "Izvezi Payjoin zapise", + "generating_csv": "Generating CSV...", "extend_session": "Produži sesiju", "external": "Vanjski", "external_wallet": "Vanjski novčanik", diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb index b72cc51ea..c003dc3bb 100644 --- a/res/values/strings_hy.arb +++ b/res/values/strings_hy.arb @@ -453,11 +453,14 @@ "expires": "Լրանում է", "expiresOn": "Լրանում է", "expiry_and_validity": "Ժամկետ և վավերականություն", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Արտահանել պահուստային պատճենը", + "export_csv": "Export CSV", "export_lightning_logs": "Արտահանել Lightning-ի տեղեկամատյանները", "export_logs": "Արտահանել տեղեկամատյանները", "export_outputs": "Արտահանել ելքերը", "export_payjoin_logs": "Արտահանել Payjoin-ի մատյանները", + "generating_csv": "Generating CSV...", "extend_session": "Երկարացնել սեսիան", "external": "Արտաքին", "external_wallet": "Արտաքին դրամապանակ", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index be26c93b0..b89942b19 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -453,11 +453,14 @@ "expires": "Kedaluwarsa", "expiresOn": "Kedaluwarsa pada", "expiry_and_validity": "Kedaluwarsa dan Validitas", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Ekspor cadangan", + "export_csv": "Export CSV", "export_lightning_logs": "Ekspor log Lightning", "export_logs": "Ekspor log", "export_outputs": "Ekspor keluaran", "export_payjoin_logs": "Ekspor log Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Perpanjang Sesi", "external": "Eksternal", "external_wallet": "Dompet eksternal", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 6f0e1ad61..b90718c40 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -453,11 +453,14 @@ "expires": "Scade", "expiresOn": "Scade il", "expiry_and_validity": "Scadenza e validità", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Esporta backup", + "export_csv": "Export CSV", "export_lightning_logs": "Esporta i log di Lightning", "export_logs": "Esporta registri", "export_outputs": "Esporta output", "export_payjoin_logs": "Esporta i log Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Estendi la sessione", "external": "Esterno", "external_wallet": "Portafoglio esterno", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index a470e459c..0db5a0617 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -453,11 +453,14 @@ "expires": "有効期限", "expiresOn": "有効期限", "expiry_and_validity": "有効期限と有効性", + "csv_nothing_to_export": "Nothing to export", "export_backup": "バックアップをエクスポート", + "export_csv": "Export CSV", "export_lightning_logs": "Lightningログをエクスポート", "export_logs": "ログをエクスポート", "export_outputs": "アウトプットをエクスポート", "export_payjoin_logs": "Payjoinログをエクスポート", + "generating_csv": "Generating CSV...", "extend_session": "セッションを延長", "external": "外部", "external_wallet": "外部ウォレット", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 80f132fed..2cbfd964e 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -453,11 +453,14 @@ "expires": "만료됨", "expiresOn": "만료일", "expiry_and_validity": "만료 및 유효 기간", + "csv_nothing_to_export": "Nothing to export", "export_backup": "백업 내보내기", + "export_csv": "Export CSV", "export_lightning_logs": "Lightning 로그 내보내기", "export_logs": "로그 내보내기", "export_outputs": "출력 내보내기", "export_payjoin_logs": "Payjoin 로그 내보내기", + "generating_csv": "Generating CSV...", "extend_session": "세션 연장", "external": "외부", "external_wallet": "외부 지갑", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 1b6192785..288f3dc9f 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -453,11 +453,14 @@ "expires": "သက်တမ်းကုန်ဆုံးသည်", "expiresOn": "သက်တမ်းကုန်ဆုံးမည့်ရက်", "expiry_and_validity": "သက်တမ်းကုန်ဆုံးရက်နှင့် အကျုံးဝင်မှု", + "csv_nothing_to_export": "Nothing to export", "export_backup": "အရန်ကူးကို ထုတ်ယူရန်", + "export_csv": "Export CSV", "export_lightning_logs": "Lightning မှတ်တမ်းများကို ထုတ်ယူပါ", "export_logs": "မှတ်တမ်းများကို ထုတ်ယူရန်", "export_outputs": "Outputs များကို ထုတ်ယူရန်", "export_payjoin_logs": "Payjoin မှတ်တမ်းများကို တင်ပို့ပါ", + "generating_csv": "Generating CSV...", "extend_session": "စက်ရှင် သက်တမ်းတိုးရန်", "external": "ပြင်ပ", "external_wallet": "ပြင်ပ Wallet", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index a1a085f06..12d1bb54b 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -451,11 +451,14 @@ "expires": "Verloopt", "expiresOn": "Verloopt op", "expiry_and_validity": "Vervaldatum en geldigheid", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Back-up exporteren", + "export_csv": "Export CSV", "export_lightning_logs": "Lightning-logboeken exporteren", "export_logs": "Exporteer logboeken", "export_outputs": "Outputs exporteren", "export_payjoin_logs": "Payjoin-logboeken exporteren", + "generating_csv": "Generating CSV...", "extend_session": "Sessie verlengen", "external": "Extern", "external_wallet": "Externe wallet", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index a70d9f721..5389b181d 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -452,11 +452,14 @@ "expires": "Wygasa", "expiresOn": "Wygasa", "expiry_and_validity": "Termin ważności i ważność", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Eksportuj kopię zapasową", + "export_csv": "Export CSV", "export_lightning_logs": "Eksportuj logi Lightning", "export_logs": "Eksportuj logi", "export_outputs": "Eksportuj wyjścia", "export_payjoin_logs": "Eksportuj logi Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Przedłuż sesję", "external": "Zewnętrzny", "external_wallet": "Zewnętrzny portfel", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 902b43c95..118cd47db 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -453,11 +453,14 @@ "expires": "Expira", "expiresOn": "Expira em", "expiry_and_validity": "Expiração e validade", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Exportar backup", + "export_csv": "Export CSV", "export_lightning_logs": "Exportar logs do Lightning", "export_logs": "Exportar logs", "export_outputs": "Exportar saídas", "export_payjoin_logs": "Exportar logs do Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Estender sessão", "external": "Externo", "external_wallet": "Carteira externa", diff --git a/res/values/strings_pt_BR.arb b/res/values/strings_pt_BR.arb index 878799ef9..2f5e17e30 100644 --- a/res/values/strings_pt_BR.arb +++ b/res/values/strings_pt_BR.arb @@ -451,11 +451,14 @@ "expires": "Expira", "expiresOn": "Expira em", "expiry_and_validity": "Expiração e Validade", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Exportar backup", + "export_csv": "Export CSV", "export_lightning_logs": "Exportar logs do Lightning", "export_logs": "Exportar logs", "export_outputs": "Exportar saídas", "export_payjoin_logs": "Exportar logs do Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Estender sessão", "external": "Externo", "external_wallet": "Carteira externa", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index d48f52f3f..828e899bb 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -453,11 +453,14 @@ "expires": "Истекает", "expiresOn": "Истекает", "expiry_and_validity": "Срок действия и действительность", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Экспорт резервной копии", + "export_csv": "Export CSV", "export_lightning_logs": "Экспорт логов Lightning", "export_logs": "Экспорт журналов", "export_outputs": "Экспорт выходов", "export_payjoin_logs": "Экспорт журналов Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Продлить сессию", "external": "Внешний", "external_wallet": "Внешний кошелёк", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index cc66b12e7..f0abd0399 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -453,11 +453,14 @@ "expires": "หมดอายุ", "expiresOn": "หมดอายุวันที่", "expiry_and_validity": "วันหมดอายุและระยะเวลาที่มีผล", + "csv_nothing_to_export": "Nothing to export", "export_backup": "ส่งออกข้อมูลสำรอง", + "export_csv": "Export CSV", "export_lightning_logs": "ส่งออกล็อก Lightning", "export_logs": "ส่งออกบันทึก", "export_outputs": "ส่งออกเอาต์พุต", "export_payjoin_logs": "ส่งออกล็อก Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "ขยายเซสชัน", "external": "ภายนอก", "external_wallet": "กระเป๋าเงินภายนอก", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index ffdd9e49b..71a87c25a 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -453,11 +453,14 @@ "expires": "Mag-e-expire", "expiresOn": "Mag-e-expire sa", "expiry_and_validity": "Pag-expire at Pagiging Balido", + "csv_nothing_to_export": "Nothing to export", "export_backup": "I-export ang backup", + "export_csv": "Export CSV", "export_lightning_logs": "I-export ang mga log ng Lightning", "export_logs": "I-export ang mga log", "export_outputs": "I-export ang mga output", "export_payjoin_logs": "I-export ang mga Payjoin log", + "generating_csv": "Generating CSV...", "extend_session": "Palawigin ang session", "external": "Panlabas", "external_wallet": "Panlabas na wallet", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index ead936751..346b35d0d 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -453,11 +453,14 @@ "expires": "Sona erme tarihi", "expiresOn": "Şu tarihte sona erer", "expiry_and_validity": "Son Kullanma ve Geçerlilik", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Yedeklemeyi dışa aktar", + "export_csv": "Export CSV", "export_lightning_logs": "Lightning günlüklerini dışa aktar", "export_logs": "Günlükleri dışa aktar", "export_outputs": "Çıktıları dışa aktar", "export_payjoin_logs": "Payjoin günlüklerini dışa aktar", + "generating_csv": "Generating CSV...", "extend_session": "Oturumu Uzat", "external": "Harici", "external_wallet": "Harici cüzdan", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 7478683a7..769221508 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -453,11 +453,14 @@ "expires": "Термін дії закінчується", "expiresOn": "Дійсний до", "expiry_and_validity": "Термін дії та чинність", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Експортувати резервну копію", + "export_csv": "Export CSV", "export_lightning_logs": "Експортувати логи Lightning", "export_logs": "Експорт журналів", "export_outputs": "Експорт виходів", "export_payjoin_logs": "Експорт журналів Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Подовжити сеанс", "external": "Зовнішній", "external_wallet": "Зовнішній гаманець", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index d9607eb8c..9285c8e7e 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -453,11 +453,14 @@ "expires": "میعاد ختم ہوتا ہے", "expiresOn": "میعاد ختم ہونے کی تاریخ", "expiry_and_validity": "میعادِ ختم اور درستگی", + "csv_nothing_to_export": "Nothing to export", "export_backup": "بیک اپ ایکسپورٹ کریں", + "export_csv": "Export CSV", "export_lightning_logs": "لائٹننگ لاگز برآمد کریں", "export_logs": "لاگز برآمد کریں", "export_outputs": "آؤٹ پٹس ایکسپورٹ کریں", "export_payjoin_logs": "Payjoin لاگز ایکسپورٹ کریں", + "generating_csv": "Generating CSV...", "extend_session": "سیشن میں توسیع کریں", "external": "بیرونی", "external_wallet": "بیرونی والیٹ", diff --git a/res/values/strings_vi.arb b/res/values/strings_vi.arb index 3c4d7a4c9..5ce21b68c 100644 --- a/res/values/strings_vi.arb +++ b/res/values/strings_vi.arb @@ -452,11 +452,14 @@ "expires": "Hết hạn", "expiresOn": "Hết hạn vào", "expiry_and_validity": "Thời hạn và hiệu lực", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Xuất bản sao lưu", + "export_csv": "Export CSV", "export_lightning_logs": "Xuất nhật ký Lightning", "export_logs": "Xuất nhật ký", "export_outputs": "Xuất đầu ra", "export_payjoin_logs": "Xuất nhật ký Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Gia hạn phiên", "external": "Bên ngoài", "external_wallet": "Ví ngoài", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index d29fd32fc..29ebbe6c4 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -454,11 +454,14 @@ "expires": "Yóò parí", "expiresOn": "Yóò parí ní", "expiry_and_validity": "Ọjọ́ ìparí àti ìmúlò", + "csv_nothing_to_export": "Nothing to export", "export_backup": "Gbé ẹ̀dà àfipamọ́ jáde", + "export_csv": "Export CSV", "export_lightning_logs": "Gbe àwọn àkọọlẹ Lightning jáde", "export_logs": "Gbe àwọn ìwé-àkọsílẹ̀ jáde", "export_outputs": "Gbe àwọn output jáde", "export_payjoin_logs": "Gbejade àwọn ìkọ̀ọ́lẹ̀ Payjoin", + "generating_csv": "Generating CSV...", "extend_session": "Fa igba iṣẹ́ pọ̀ síi", "external": "Ita", "external_wallet": "Apamọwọ ita", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 960e29a99..766d79737 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -453,11 +453,14 @@ "expires": "到期", "expiresOn": "到期于", "expiry_and_validity": "有效期和有效性", + "csv_nothing_to_export": "Nothing to export", "export_backup": "导出备份", + "export_csv": "Export CSV", "export_lightning_logs": "导出 Lightning 日志", "export_logs": "导出日志", "export_outputs": "导出输出", "export_payjoin_logs": "导出 Payjoin 日志", + "generating_csv": "Generating CSV...", "extend_session": "延长会话", "external": "外部", "external_wallet": "外部钱包", diff --git a/res/values/strings_zh_tw.arb b/res/values/strings_zh_tw.arb index cc672e0ba..c85a7b648 100644 --- a/res/values/strings_zh_tw.arb +++ b/res/values/strings_zh_tw.arb @@ -392,11 +392,14 @@ "expires": "到期", "expiresOn": "到期日", "expiry_and_validity": "到期與有效性", + "csv_nothing_to_export": "Nothing to export", "export_backup": "匯出備份", + "export_csv": "Export CSV", "export_lightning_logs": "匯出 Lightning 記錄", "export_logs": "匯出記錄", "export_outputs": "匯出輸出項", "export_payjoin_logs": "匯出 Payjoin 日誌", + "generating_csv": "Generating CSV...", "extend_session": "延長登入階段", "extra_id": "額外 ID:", "extracted_address_content": "您將發送資金至\n${recipient_name}",