diff --git a/assets/l10n/en_IN.json b/assets/l10n/en_IN.json index c2a8f549..06271fc8 100644 --- a/assets/l10n/en_IN.json +++ b/assets/l10n/en_IN.json @@ -152,6 +152,7 @@ "transactions.query.filter.categories.all": "All Categories", "transactions.query.filter.groupBy": "Group by", "transactions.query.filter.sort": "Sort", + "transactions.query.filter.currency": "Currency", "transactions.count": "{} transactions", "category": "Category", diff --git a/assets/l10n/en_US.json b/assets/l10n/en_US.json index 0ca2c56e..68adc89f 100644 --- a/assets/l10n/en_US.json +++ b/assets/l10n/en_US.json @@ -152,6 +152,7 @@ "transactions.query.filter.categories.all": "All Categories", "transactions.query.filter.groupBy": "Group by", "transactions.query.filter.sort": "Sort", + "transactions.query.filter.currency": "Currency", "transactions.count": "{} transactions", "category": "Category", diff --git a/assets/l10n/it_IT.json b/assets/l10n/it_IT.json index e0012a2e..90516e6b 100644 --- a/assets/l10n/it_IT.json +++ b/assets/l10n/it_IT.json @@ -152,6 +152,7 @@ "transactions.query.filter.categories.all": "Tutte le categorie", "transactions.query.filter.groupBy": "Raggruppa per", "transactions.query.filter.sort": "Ordina", + "transactions.query.filter.currency": "Valuta", "transactions.count": "{count} transazioni", "category": "Categoria", diff --git a/assets/l10n/mn_MN.json b/assets/l10n/mn_MN.json index 5d8c9117..fdec9fb0 100644 --- a/assets/l10n/mn_MN.json +++ b/assets/l10n/mn_MN.json @@ -152,6 +152,7 @@ "transactions.query.filter.categories.all": "Бүх ангилал", "transactions.query.filter.groupBy": "Бүлэглэх нэгж", "transactions.query.filter.sort": "Эрэмбэ", + "transactions.query.filter.currency": "Валют", "transactions.count": "{} гүйлгээ", "category": "Ангилал", diff --git a/assets/l10n/tr_TR.json b/assets/l10n/tr_TR.json index 99aafc45..3a6fe247 100644 --- a/assets/l10n/tr_TR.json +++ b/assets/l10n/tr_TR.json @@ -152,6 +152,7 @@ "transactions.query.filter.categories.all": "Tüm Kategoriler", "transactions.query.filter.groupBy": "Şuna göre grupla", "transactions.query.filter.sort": "Sırala", + "transactions.query.filter.currency": "Para birimi", "transactions.count": "{} İşlemler", "category": "Kategori", diff --git a/lib/routes/account/account_edit_page.dart b/lib/routes/account/account_edit_page.dart index d85e0ebd..d82eb72a 100644 --- a/lib/routes/account/account_edit_page.dart +++ b/lib/routes/account/account_edit_page.dart @@ -523,6 +523,8 @@ class _AccountEditPageState extends State { child: Text("account.delete.warning".t(context, txnCount)), ); + if (!mounted) return; + if (confirmation == true) { await export( showShareDialog: false, @@ -542,11 +544,10 @@ class _AccountEditPageState extends State { await ObjectBox().box().removeAsync(_currentlyEditing!.id); } catch (e) { log("[Account Page] Failed to delete account ${_currentlyEditing!.name} (${_currentlyEditing!.uuid}) due to:\n$e"); - } finally { - if (mounted) { - context.pop(); - } } } + + if (!mounted) return; + context.pop(); } } diff --git a/lib/widgets/default_transaction_filter_head.dart b/lib/widgets/default_transaction_filter_head.dart index b3010a59..0cc32437 100644 --- a/lib/widgets/default_transaction_filter_head.dart +++ b/lib/widgets/default_transaction_filter_head.dart @@ -1,9 +1,13 @@ +import "dart:developer"; + +import "package:flow/data/currencies.dart"; import "package:flow/data/transactions_filter.dart"; import "package:flow/entity/account.dart"; import "package:flow/entity/category.dart"; import "package:flow/objectbox.dart"; import "package:flow/objectbox/actions.dart"; import "package:flow/utils/optional.dart"; +import "package:flow/widgets/select_multi_currency_sheet.dart"; import "package:flow/widgets/transaction_filter_head.dart"; import "package:flow/widgets/transaction_filter_head/select_group_range_sheet.dart"; import "package:flow/widgets/transaction_filter_head/select_multi_account_sheet.dart"; @@ -96,6 +100,15 @@ class _DefaultTransactionsFilterHeadState ? _filter.categories : null, ), + TransactionFilterChip>( + translationKey: "transactions.query.filter.currency", + avatar: const Icon(Symbols.universal_currency_alt_rounded), + onSelect: onSelectCurrency, + defaultValue: widget.defaultFilter.currencies, + value: _filter.currencies?.isNotEmpty == true + ? _filter.currencies + : null, + ), TransactionFilterChip( translationKey: "transactions.query.filter.groupBy", avatar: const Icon(Symbols.atr_rounded), @@ -160,6 +173,32 @@ class _DefaultTransactionsFilterHeadState } } + void onSelectCurrency() async { + final Set possibleCurrencies = + ObjectBox().getAccounts().map((account) => account.currency).toSet(); + + final List? newCurrencies = + await showModalBottomSheet>( + context: context, + builder: (context) => SelectMultiCurrencySheet( + currencies: possibleCurrencies + .map((code) => iso4217CurrenciesGrouped[code]) + .nonNulls + .toList(), + currentlySelected: filter.currencies, + ), + isScrollControlled: true, + ); + + log("newCurrencies $newCurrencies"); + + if (newCurrencies != null) { + setState(() { + filter = filter.copyWithOptional(currencies: Optional(newCurrencies)); + }); + } + } + void onSelectGroupBy() async { final TransactionGroupRange? newGroupBy = await showModalBottomSheet( diff --git a/lib/widgets/select_multi_currency_sheet.dart b/lib/widgets/select_multi_currency_sheet.dart new file mode 100644 index 00000000..e5aa9fb4 --- /dev/null +++ b/lib/widgets/select_multi_currency_sheet.dart @@ -0,0 +1,113 @@ +import "package:flow/data/currencies.dart"; +import "package:flow/l10n/extensions.dart"; +import "package:flow/theme/theme.dart"; +import "package:flow/utils/utils.dart"; +import "package:flow/widgets/general/modal_overflow_bar.dart"; +import "package:flow/widgets/general/modal_sheet.dart"; +import "package:flutter/material.dart"; +import "package:go_router/go_router.dart"; +import "package:material_symbols_icons/symbols.dart"; + +/// Pops with a list of valid [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code [List] +class SelectMultiCurrencySheet extends StatefulWidget { + final List? currentlySelected; + final List currencies; + + const SelectMultiCurrencySheet({ + super.key, + required this.currencies, + this.currentlySelected, + }); + + @override + State createState() => + _SelectMultiCurrencySheetState(); +} + +class _SelectMultiCurrencySheetState extends State { + late Set selectedUuids; + + @override + void initState() { + super.initState(); + selectedUuids = Set.from(widget.currentlySelected ?? (const [])); + } + + @override + void didUpdateWidget(SelectMultiCurrencySheet oldWidget) { + if (widget.currentlySelected != oldWidget.currentlySelected) { + selectedUuids = Set.from(widget.currentlySelected ?? (const [])); + } + super.didUpdateWidget(oldWidget); + } + + @override + Widget build(BuildContext context) { + return ModalSheet.scrollable( + title: Text("account.edit.selectCurrency".t(context)), + scrollableContentMaxHeight: MediaQuery.of(context).size.height * 0.4 - + MediaQuery.of(context).viewInsets.vertical, + trailing: ModalOverflowBar( + alignment: MainAxisAlignment.end, + children: [ + TextButton.icon( + onPressed: () => context.pop([]), + icon: const Icon(Symbols.block_rounded), + label: Text("transactions.query.clearSelection".t(context)), + ), + TextButton.icon( + onPressed: pop, + icon: const Icon(Symbols.check), + label: Text("general.done".t(context)), + ), + ], + ), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: widget.currencies.map((currency) { + final CurrencyData transformedCurrencyData = + iso4217CurrenciesGrouped[currency.code]!; + + return CheckboxListTile.adaptive( + value: selectedUuids.contains(transformedCurrencyData.code), + title: Text( + transformedCurrencyData.name, + ), + subtitle: Text( + transformedCurrencyData.country.titleCase(), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + secondary: Text( + transformedCurrencyData.code, + style: context.textTheme.bodyLarge?.copyWith( + fontFeatures: [const FontFeature.tabularFigures()], + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + onChanged: (value) => select(transformedCurrencyData.code, value), + ); + }).toList(), + ), + ), + ); + } + + void select(String code, bool? selected) { + if (selected == null) return; + + if (selected) { + selectedUuids.add(code); + } else { + selectedUuids.remove(code); + } + + setState(() {}); + } + + void pop() { + context.pop(selectedUuids.toList()); + } +} diff --git a/lib/widgets/transaction_filter_head/select_multi_account_sheet.dart b/lib/widgets/transaction_filter_head/select_multi_account_sheet.dart index 7e04fb86..fad8154c 100644 --- a/lib/widgets/transaction_filter_head/select_multi_account_sheet.dart +++ b/lib/widgets/transaction_filter_head/select_multi_account_sheet.dart @@ -79,9 +79,6 @@ class _SelectMultiAccountSheetState extends State { ...widget.accounts.map( (account) => CheckboxListTile.adaptive( title: Text(account.name), - // leading: FlowIcon(account.icon), - // trailing: const Icon(Symbols.chevron_right_rounded), - // onTap: () => context.pop(account), value: selectedUuids.contains(account.uuid), onChanged: (value) => select(account.uuid, value), ),