diff --git a/budget/assets/static/Convert.py b/budget/assets/static/Convert.py index 39c562f0..ac078e03 100644 --- a/budget/assets/static/Convert.py +++ b/budget/assets/static/Convert.py @@ -24,11 +24,10 @@ for currencyInfo in data2: if (currencyInfo["Code"]).lower()==currency.lower(): found = True - result[currency] = { - "Currency": currencyInfo["Currency"], - "Code": currencyInfo["Code"], - "Symbol": currencyInfo["Symbol"], - } + result[currency] = {} + for key in ["Currency", "Code", "Symbol"]: + if key in currencyInfo: + result[currency][key] = currencyInfo[key] if("CountryName" in currencyInfo): result[currency]["CountryName"] = currencyInfo["CountryName"] for country in countries: diff --git a/budget/assets/static/currenciesInfo.json b/budget/assets/static/currenciesInfo.json index 2148c947..99347ed5 100644 --- a/budget/assets/static/currenciesInfo.json +++ b/budget/assets/static/currenciesInfo.json @@ -764,8 +764,7 @@ "Flag": "https://www.currencyremitapp.com/wp-content/themes/currencyremitapp/images/countryimages/uzbekistan.png", "CountryName": "Uzbekistan", "Currency": "Som", - "Code": "UZS", - "Symbol": "лв" + "Code": "UZS" }, { "Flag": "https://www.currencyremitapp.com/wp-content/themes/currencyremitapp/images/countryimages/venezuela.png", diff --git a/budget/assets/static/generated/currencies.json b/budget/assets/static/generated/currencies.json index 26d89ea9..3bc69b2e 100644 --- a/budget/assets/static/generated/currencies.json +++ b/budget/assets/static/generated/currencies.json @@ -2605,7 +2605,6 @@ "uzs": { "Currency": "Som", "Code": "UZS", - "Symbol": "лв", "CountryName": "Uzbekistan", "CountryCode": "UZ" }, diff --git a/budget/lib/functions.dart b/budget/lib/functions.dart index 6fdd01ae..08977e39 100644 --- a/budget/lib/functions.dart +++ b/budget/lib/functions.dart @@ -211,7 +211,8 @@ String convertToMoney(AllWallets allWallets, double amount, final NumberFormat formatter; if (getCustomNumberFormat != null) { - formatter = getCustomNumberFormat(decimalDigits, locale, symbol); + formatter = getCustomNumberFormat( + decimalDigits, locale, useCustomNumberFormat ? "" : symbol); } else if (forceDefaultNumberFormatter == false && (forceCompactNumberFormatter || appStateSettings["shortNumberFormat"] == "compact")) { @@ -221,7 +222,6 @@ String convertToMoney(AllWallets allWallets, double amount, symbol: useCustomNumberFormat ? "" : symbol, ); formatter.significantDigitsInUse = false; - formatter.currencyName = symbol; } else { formatter = NumberFormat.currency( decimalDigits: decimalDigits, @@ -239,11 +239,11 @@ String convertToMoney(AllWallets allWallets, double amount, addCurrencyName = true; } String formatOutput = formatter.format(amount).trim(); + String? currencyName; if (addCurrencyName == true && currencyKey != null) { - formatOutput = formatOutput + " " + currencyKey.toUpperCase(); + currencyName = " " + currencyKey.toUpperCase(); } else if (addCurrencyName == true) { - formatOutput = formatOutput + - " " + + currencyName = " " + (allWallets.indexedByPk[appStateSettings["selectedWalletPk"]] ?.currency ?? "") @@ -252,11 +252,15 @@ String convertToMoney(AllWallets allWallets, double amount, if (useCustomNumberFormat) { formatOutput = formatOutputWithNewDelimiterAndDecimal( - formatOutput, - appStateSettings["numberFormatDelimiter"], - appStateSettings["numberFormatDecimal"], - symbol, + amount: finalNumber ?? amount, + currencyName: currencyName, + input: formatOutput, + delimiter: appStateSettings["numberFormatDelimiter"], + decimal: appStateSettings["numberFormatDecimal"], + symbol: symbol, ); + } else if (useCustomNumberFormat == false && currencyName != null) { + formatOutput = formatOutput + currencyName; } if (editFormattedOutput != null) { @@ -287,16 +291,31 @@ String convertToMoney(AllWallets allWallets, double amount, // return currency.format(amount); } -String formatOutputWithNewDelimiterAndDecimal( - String input, String delimiter, String decimal, String symbol) { +String formatOutputWithNewDelimiterAndDecimal({ + required double amount, + required String input, + required String delimiter, + required String decimal, + required String symbol, + required String? currencyName, +}) { // Use a placeholder input = input.replaceAll(".", "\uFFFD"); input = input.replaceAll(",", delimiter); input = input.replaceAll("\uFFFD", decimal); + String negativeSign = ""; + if (amount < 0) { + input = input.replaceRange(0, 1, ""); + negativeSign = "-"; + } if (appStateSettings["numberFormatCurrencyFirst"] == false) { - return input + symbol; + return negativeSign + + input + + (symbol.length > 0 ? "  " : "") + + symbol + + (currencyName ?? ""); } else { - return symbol + input; + return negativeSign + symbol + input + (currencyName ?? ""); } } @@ -713,11 +732,13 @@ String getWordedNumber( value, forceHideCurrencyName: true, addCurrencyName: false, - editFormattedOutput: (output) { - return getCurrencyString(Provider.of(context)) + output; - }, getCustomNumberFormat: (decimalDigits, locale, currencySymbol) { - final NumberFormat formatter = NumberFormat.compact(locale: locale); + final NumberFormat formatter = NumberFormat.compactCurrency( + locale: locale, + decimalDigits: decimalDigits, + symbol: currencySymbol, + ); + formatter.significantDigitsInUse = false; formatter.maximumFractionDigits = value.abs() < 1000 ? value.abs() < 10 ? (decimalDigits ?? 2) @@ -725,8 +746,6 @@ String getWordedNumber( : 1; formatter.minimumFractionDigits = value.abs() < 10 ? (decimalDigits ?? 2) : 0; - formatter.significantDigitsInUse = false; - formatter.currencyName = currencySymbol; return formatter; }, ); diff --git a/budget/lib/pages/addBudgetPage.dart b/budget/lib/pages/addBudgetPage.dart index 53de39db..d24aa8c9 100644 --- a/budget/lib/pages/addBudgetPage.dart +++ b/budget/lib/pages/addBudgetPage.dart @@ -648,7 +648,7 @@ class _AddBudgetPageState extends State { ], ), ], - overlay: Align( + staticOverlay: Align( alignment: Alignment.bottomCenter, child: selectedTitle == "" || selectedTitle == null ? SaveBottomButton( diff --git a/budget/lib/pages/addCategoryPage.dart b/budget/lib/pages/addCategoryPage.dart index 05708e77..d3653acd 100644 --- a/budget/lib/pages/addCategoryPage.dart +++ b/budget/lib/pages/addCategoryPage.dart @@ -377,7 +377,7 @@ class _AddCategoryPageState extends State ) : SizedBox.shrink() ], - overlay: Align( + staticOverlay: Align( alignment: Alignment.bottomCenter, child: selectedTitle == "" || selectedTitle == null ? SaveBottomButton( diff --git a/budget/lib/pages/addObjectivePage.dart b/budget/lib/pages/addObjectivePage.dart index ed03a5ae..ebf701a8 100644 --- a/budget/lib/pages/addObjectivePage.dart +++ b/budget/lib/pages/addObjectivePage.dart @@ -478,7 +478,7 @@ class _AddObjectivePageState extends State ], ), ], - overlay: Align( + staticOverlay: Align( alignment: Alignment.bottomCenter, child: selectedTitle == "" || selectedTitle == null ? SaveBottomButton( diff --git a/budget/lib/pages/addTransactionPage.dart b/budget/lib/pages/addTransactionPage.dart index c5219b41..ba68087f 100644 --- a/budget/lib/pages/addTransactionPage.dart +++ b/budget/lib/pages/addTransactionPage.dart @@ -93,6 +93,7 @@ class AddTransactionPage extends StatefulWidget { this.selectedType, this.selectedObjective, this.selectedIncome, + this.useCategorySelectedIncome = false, this.selectedAmount, this.selectedTitle, this.selectedCategory, @@ -112,6 +113,7 @@ class AddTransactionPage extends StatefulWidget { final Objective? selectedObjective; final RoutesToPopAfterDelete routesToPopAfterDelete; final bool? selectedIncome; + final bool useCategorySelectedIncome; final double? selectedAmount; final String? selectedTitle; final TransactionCategory? selectedCategory; @@ -814,6 +816,8 @@ class _AddTransactionPageState extends State } if (widget.selectedCategory != null) { selectedCategory = widget.selectedCategory; + if (widget.useCategorySelectedIncome) + selectedIncome = selectedCategory?.income ?? selectedIncome; } if (widget.selectedSubCategory != null) { selectedSubCategory = widget.selectedSubCategory; @@ -2006,7 +2010,8 @@ class _AddTransactionPageState extends State ) : SizedBox.shrink() ], - overlay: Align( + overlay: MinimizeKeyboardFABOverlay(isEnabled: notesInputFocused), + staticOverlay: Align( alignment: Alignment.bottomCenter, child: Row( children: [ @@ -2096,35 +2101,6 @@ class _AddTransactionPageState extends State key: ValueKey(2), ), ), - AnimatedSizeSwitcher( - child: notesInputFocused && getPlatform() == PlatformOS.isIOS - ? WidgetSizeBuilder( - widgetBuilder: (Size? size) { - return Container( - key: ValueKey(1), - width: size?.width, - child: SaveBottomButton( - margin: EdgeInsets.only(left: 5), - color: isTransactionActionDealtWith( - createTransaction()) - ? Theme.of(context).colorScheme.primary - : null, - labelColor: isTransactionActionDealtWith( - createTransaction()) - ? Theme.of(context).colorScheme.onPrimary - : null, - label: "done".tr(), - onTap: () async { - FocusManager.instance.primaryFocus?.unfocus(); - }, - ), - ); - }, - ) - : Container( - key: ValueKey(2), - ), - ), ], ), ), @@ -4282,12 +4258,14 @@ class _TransactionNotesTextInputState extends State { ), HorizontalBreak( padding: EdgeInsets.zero, - color: dynamicPastel( - context, - Theme.of(context).colorScheme.secondaryContainer, - amount: 0.1, - inverse: true, - ), + color: appStateSettings["materialYou"] + ? dynamicPastel( + context, + Theme.of(context).colorScheme.secondaryContainer, + amount: 0.1, + inverse: true, + ) + : getColor(context, "lightDarkAccent"), ), LinkInNotes( color: (appStateSettings["materialYou"] diff --git a/budget/lib/pages/addWalletPage.dart b/budget/lib/pages/addWalletPage.dart index 60931447..d3b24d8b 100644 --- a/budget/lib/pages/addWalletPage.dart +++ b/budget/lib/pages/addWalletPage.dart @@ -437,7 +437,7 @@ class _AddWalletPageState extends State { ], ), ], - overlay: Align( + staticOverlay: Align( alignment: Alignment.bottomCenter, child: selectedTitle == "" || selectedTitle == null ? SaveBottomButton( diff --git a/budget/lib/pages/autoTransactionsPageEmail.dart b/budget/lib/pages/autoTransactionsPageEmail.dart index d19550e3..13fa4bf5 100644 --- a/budget/lib/pages/autoTransactionsPageEmail.dart +++ b/budget/lib/pages/autoTransactionsPageEmail.dart @@ -131,6 +131,7 @@ Future queueTransactionFromMessage(String messageString) async { pushRoute( navigatorKey.currentContext!, AddTransactionPage( + useCategorySelectedIncome: true, routesToPopAfterDelete: RoutesToPopAfterDelete.None, selectedAmount: amountDouble, selectedTitle: title, @@ -520,7 +521,7 @@ Future parseEmailsInBackground(context, Transaction transactionToAdd = Transaction( transactionPk: "-1", name: title, - amount: (amountDouble).abs() * -1, + amount: (amountDouble).abs() * (selectedCategory.income ? 1 : -1), note: "", categoryFk: selectedCategory.categoryPk, walletFk: appStateSettings["selectedWalletPk"], diff --git a/budget/lib/pages/billSplitter.dart b/budget/lib/pages/billSplitter.dart index 832e4a58..99726b1f 100644 --- a/budget/lib/pages/billSplitter.dart +++ b/budget/lib/pages/billSplitter.dart @@ -1156,7 +1156,7 @@ class _AddBillItemPageState extends State { }, ), ], - overlay: Align( + staticOverlay: Align( alignment: Alignment.bottomCenter, child: SaveBottomButton( label: widget.billSplitterItem == null @@ -1365,7 +1365,7 @@ class SummaryPage extends StatelessWidget { return PageFramework( title: "summary".tr(), dragDownToDismiss: true, - overlay: Align( + staticOverlay: Align( alignment: Alignment.bottomCenter, child: SaveBottomButton( label: "generate-loan-transactions".tr(), diff --git a/budget/lib/pages/budgetPage.dart b/budget/lib/pages/budgetPage.dart index 90452a93..cf3ed5ac 100644 --- a/budget/lib/pages/budgetPage.dart +++ b/budget/lib/pages/budgetPage.dart @@ -296,7 +296,7 @@ class _BudgetPageContentState extends State<_BudgetPageContent> { routesToPopAfterDelete: RoutesToPopAfterDelete.One, ), color: budgetColorScheme.secondary, - colorPlus: budgetColorScheme.onSecondary, + colorIcon: budgetColorScheme.onSecondary, ), ), actions: [ diff --git a/budget/lib/pages/objectivePage.dart b/budget/lib/pages/objectivePage.dart index 9f02ba5c..bc2e23e5 100644 --- a/budget/lib/pages/objectivePage.dart +++ b/budget/lib/pages/objectivePage.dart @@ -221,7 +221,7 @@ class _ObjectivePageContentState extends State<_ObjectivePageContent> { selectedIncome: widget.objective.income, ), color: objectiveColorScheme.secondary, - colorPlus: objectiveColorScheme.onSecondary, + colorIcon: objectiveColorScheme.onSecondary, ), ), expandedHeight: 56, diff --git a/budget/lib/pages/settingsPage.dart b/budget/lib/pages/settingsPage.dart index 082bb286..1e1a0c14 100644 --- a/budget/lib/pages/settingsPage.dart +++ b/budget/lib/pages/settingsPage.dart @@ -1173,10 +1173,14 @@ class _SetNumberFormatPopupState extends State { children: [ TextFont( textAlign: TextAlign.center, - fontSize: 30, + fontSize: 28, fontWeight: FontWeight.bold, - text: convertToMoney(allWallets, 1234.56, - forceNonCustomNumberFormat: true), + text: convertToMoney( + allWallets, + -1234.56, + forceNonCustomNumberFormat: true, + addCurrencyName: true, + ), ), ], ), @@ -1231,24 +1235,43 @@ class _SetNumberFormatPopupState extends State { ], ), ), - HorizontalBreakAbove( - padding: EdgeInsets.only(top: 25, bottom: 10), - child: SettingsContainerSwitch( - title: "short-number-format".tr(), - onSwitched: (value) { - updateSettings( - "shortNumberFormat", - value ? "compact" : null, - updateGlobalState: true, - ); - }, - initialValue: appStateSettings["shortNumberFormat"] == "compact", - enableBorderRadius: true, - icon: appStateSettings["outlinedIcons"] - ? Icons.one_k_outlined - : Icons.one_k_rounded, + SizedBox(height: 15), + HorizontalBreak(), + SettingsContainerSwitch( + title: "short-number-format".tr(), + onSwitched: (value) { + updateSettings( + "shortNumberFormat", + value ? "compact" : null, + updateGlobalState: true, + ); + }, + initialValue: appStateSettings["shortNumberFormat"] == "compact", + enableBorderRadius: true, + icon: appStateSettings["outlinedIcons"] + ? Icons.one_k_outlined + : Icons.one_k_rounded, + ), + SizedBox(height: 5), + Tappable( + borderRadius: 10, + color: Colors.transparent, + onTap: () { + Navigator.pop(context); + pushRoute(context, EditWalletsPage()); + }, + child: Padding( + padding: + const EdgeInsets.only(left: 8, right: 8, top: 5, bottom: 5), + child: TextFont( + text: "decimal-precision-edit-account-info".tr(), + fontSize: 14, + maxLines: 10, + textColor: getColor(context, "textLight"), + textAlign: TextAlign.center, + ), ), - ) + ), ], ), ); @@ -1274,8 +1297,12 @@ class _CustomNumberFormatPopupState extends State { AllWallets allWallets = Provider.of(context); String formattedNumber = convertToMoney( allWallets, - 1234.56, + -1234.56, forceCustomNumberFormat: true, + addCurrencyName: true, + customSymbol: getCurrencyString(allWallets) == "" + ? "⬚" + : getCurrencyString(allWallets), ); return Column( children: [ @@ -1284,7 +1311,7 @@ class _CustomNumberFormatPopupState extends State { child: TextFont( key: ValueKey(formattedNumber), textAlign: TextAlign.center, - fontSize: 30, + fontSize: 28, fontWeight: FontWeight.bold, text: formattedNumber, ), diff --git a/budget/lib/widgets/fab.dart b/budget/lib/widgets/fab.dart index 959c3f34..f241db05 100644 --- a/budget/lib/widgets/fab.dart +++ b/budget/lib/widgets/fab.dart @@ -11,27 +11,35 @@ import 'package:flutter/material.dart'; class FAB extends StatelessWidget { FAB({ Key? key, - required this.openPage, + this.openPage, this.onTap, this.tooltip = "", this.color, - this.colorPlus, + this.colorIcon, this.onLongPressAddAllPopup = true, + this.iconData, + this.customFabSize, + this.customBorderRadius, }) : super(key: key); - final Widget openPage; + final Widget? openPage; final String tooltip; final Function()? onTap; final Color? color; - final Color? colorPlus; + final Color? colorIcon; final bool? onLongPressAddAllPopup; + final IconData? iconData; + final double? customFabSize; + final double? customBorderRadius; @override Widget build(BuildContext context) { - double fabSize = getIsFullScreen(context) == false ? 60 : 70; + double fabSize = + customFabSize ?? (getIsFullScreen(context) == false ? 60 : 70); return OpenContainerNavigation( closedElevation: 10, - borderRadius: getIsFullScreen(context) == false ? 18 : 22, + borderRadius: + customBorderRadius ?? (getIsFullScreen(context) == false ? 18 : 22), closedColor: color != null ? color : Theme.of(context).colorScheme.secondary, button: (openContainer) { @@ -61,19 +69,18 @@ class FAB extends StatelessWidget { width: fabSize, child: Center( child: Icon( - appStateSettings["outlinedIcons"] - ? Icons.add_outlined - : Icons.add_rounded, - color: colorPlus != null - ? colorPlus - : Theme.of(context).colorScheme.onSecondary, + iconData ?? + (appStateSettings["outlinedIcons"] + ? Icons.add_outlined + : Icons.add_rounded), + color: colorIcon ?? Theme.of(context).colorScheme.onSecondary, ), ), ), ), ); }, - openPage: openPage, + openPage: openPage ?? Container(), ); } } diff --git a/budget/lib/widgets/framework/pageFramework.dart b/budget/lib/widgets/framework/pageFramework.dart index d497c85d..49a47f68 100644 --- a/budget/lib/widgets/framework/pageFramework.dart +++ b/budget/lib/widgets/framework/pageFramework.dart @@ -57,6 +57,7 @@ class PageFramework extends StatefulWidget { this.backgroundColor, this.resizeToAvoidBottomInset = false, this.overlay, + this.staticOverlay, this.scrollToTopButton = false, this.scrollToBottomButton = false, this.bottomPadding = true, @@ -97,6 +98,7 @@ class PageFramework extends StatefulWidget { final Color? backgroundColor; final bool resizeToAvoidBottomInset; final Widget? overlay; + final Widget? staticOverlay; final bool scrollToTopButton; final bool scrollToBottomButton; final bool bottomPadding; @@ -480,7 +482,7 @@ class PageFrameworkState extends State ); }, ), - widget.overlay ?? SizedBox.shrink(), + widget.staticOverlay ?? SizedBox.shrink(), ], ), ], diff --git a/budget/lib/widgets/saveBottomButton.dart b/budget/lib/widgets/saveBottomButton.dart index 9f658f45..123d41f1 100644 --- a/budget/lib/widgets/saveBottomButton.dart +++ b/budget/lib/widgets/saveBottomButton.dart @@ -1,5 +1,9 @@ +import 'package:budget/colors.dart'; import 'package:budget/functions.dart'; +import 'package:budget/struct/settings.dart'; import 'package:budget/widgets/button.dart'; +import 'package:budget/widgets/fab.dart'; +import 'package:budget/widgets/fadeIn.dart'; import 'package:budget/widgets/tappable.dart'; import 'package:flutter/material.dart'; @@ -56,9 +60,9 @@ class _SaveBottomButtonState extends State duration: Duration(milliseconds: 100), curve: Curves.easeInOutCubic, transform: Matrix4.translationValues( - 0.0, - isKeyboardOpen && !(getPlatform() == PlatformOS.isIOS) ? 100 : 0.0, - 0.0, + 0, + isKeyboardOpen ? 100 : 0, + 0, ), child: Column( mainAxisAlignment: MainAxisAlignment.end, @@ -70,7 +74,7 @@ class _SaveBottomButtonState extends State foregroundDecoration: BoxDecoration( gradient: LinearGradient( colors: [ - Theme.of(context).canvasColor.withOpacity(0.0), + Theme.of(context).canvasColor.withOpacity(0), Theme.of(context).canvasColor, ], begin: Alignment.topCenter, @@ -102,6 +106,70 @@ class _SaveBottomButtonState extends State } } +class MinimizeKeyboardFABOverlay extends StatefulWidget { + const MinimizeKeyboardFABOverlay({required this.isEnabled, super.key}); + final bool isEnabled; + @override + State createState() => + _MinimizeKeyboardFABOverlayState(); +} + +class _MinimizeKeyboardFABOverlayState extends State + with WidgetsBindingObserver { + bool isKeyboardOpen = false; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void didChangeMetrics() { + bool status = getIsKeyboardOpen(context); + if (status != isKeyboardOpen) + setState(() { + isKeyboardOpen = status; + }); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Positioned( + right: 10, + bottom: 10, + child: AnimatedContainer( + duration: Duration(milliseconds: 100), + curve: Curves.easeInOutCubic, + transform: Matrix4.translationValues( + 0, + isKeyboardOpen ? 0 : 100, + 0, + ), + child: AnimateFABDelayed( + enabled: isKeyboardOpen && widget.isEnabled, + fab: FAB( + onTap: () { + FocusManager.instance.primaryFocus?.unfocus(); + }, + customBorderRadius: 15, + customFabSize: 50, + iconData: appStateSettings["outlinedIcons"] + ? Icons.check_outlined + : Icons.check_rounded, + ), + ), + ), + ); + } +} + class KeyboardHeightAreaAnimated extends StatefulWidget { const KeyboardHeightAreaAnimated({ super.key, diff --git a/budget/lib/widgets/showChangelog.dart b/budget/lib/widgets/showChangelog.dart index 924d25de..781a74f0 100644 --- a/budget/lib/widgets/showChangelog.dart +++ b/budget/lib/widgets/showChangelog.dart @@ -29,8 +29,11 @@ String getChangelogString() { < 5.2.6 New account spending summary table Improved navigation to respective pages when filters/date ranges set in spending summary table + Improved custom number format + Added complete editing text action button in add transaction page Fix date range filters for account graph - Fix currency exchange icon + Fix line graph double currency icon in label + Fix currency exchange icons < 5.2.5 Revamped homepage pie chart section Custom number format support diff --git a/budget/pubspec.yaml b/budget/pubspec.yaml index e6176013..2e6b3b00 100644 --- a/budget/pubspec.yaml +++ b/budget/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 5.2.6+344 +version: 5.2.6+345 environment: sdk: ">= 3.0.0"