From 7943f0d91a68b804904608fcac417bdb775a8238 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 6 Oct 2026 21:46:08 -0500 Subject: [PATCH 1/6] style(balances): dart format files touched by hide balances --- .../appearance_settings_view.dart | 30 +- .../sub_widgets/my_token_select_item.dart | 39 +- .../token_view/sub_widgets/token_summary.dart | 61 ++-- .../sub_widgets/token_summary_sol.dart | 117 +++--- .../wallet_balance_toggle_sheet.dart | 41 +-- .../sub_widgets/wallet_summary_info.dart | 67 ++-- .../sub_widgets/favorite_card.dart | 141 ++++---- .../sub_widgets/desktop_wallet_summary.dart | 6 +- .../appearance_settings.dart | 108 +++--- lib/providers/global/prefs_provider.dart | 1 + lib/utilities/prefs.dart | 335 ++++++++---------- lib/widgets/managed_favorite.dart | 61 ++-- 12 files changed, 456 insertions(+), 551 deletions(-) diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/appearance_settings_view.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/appearance_settings_view.dart index aa10724a7f..ef37d5db6c 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/appearance_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/appearance_settings_view.dart @@ -59,10 +59,9 @@ class AppearanceSettingsView extends ConsumerWidget { child: Consumer( builder: (_, ref, __) { return RawMaterialButton( - splashColor: - Theme.of( - context, - ).extension()!.highlight, + splashColor: Theme.of(context) + .extension()! + .highlight, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: RoundedRectangleBorder( @@ -91,17 +90,19 @@ class AppearanceSettingsView extends ConsumerWidget { width: 40, child: DraggableSwitchButton( isOn: ref.watch( - prefsChangeNotifierProvider.select( - (value) => - value.showFavoriteWallets, - ), + prefsChangeNotifierProvider + .select( + (value) => value + .showFavoriteWallets, + ), ), onValueChanged: (newValue) { ref - .read( - prefsChangeNotifierProvider, - ) - .showFavoriteWallets = newValue; + .read( + prefsChangeNotifierProvider, + ) + .showFavoriteWallets = + newValue; }, ), ), @@ -142,9 +143,8 @@ class AppearanceSettingsView extends ConsumerWidget { SecondaryButton( label: "Add more themes", onPressed: () { - Navigator.of( - context, - ).pushNamed(ManageThemesView.routeName); + Navigator.of(context) + .pushNamed(ManageThemesView.routeName); }, ), ], diff --git a/lib/pages/token_view/sub_widgets/my_token_select_item.dart b/lib/pages/token_view/sub_widgets/my_token_select_item.dart index a00813e23f..8fe5fcf27a 100644 --- a/lib/pages/token_view/sub_widgets/my_token_select_item.dart +++ b/lib/pages/token_view/sub_widgets/my_token_select_item.dart @@ -86,14 +86,11 @@ class _MyTokenSelectItemState extends ConsumerState { final old = ref.read(tokenServiceStateProvider); // exit previous if there is one unawaited(old?.exit()); - ref.read(tokenServiceStateProvider.state).state = - Wallet.loadTokenWallet( - ethWallet: - ref.read(pWallets).getWallet(widget.walletId) - as EthereumWallet, - contract: widget.token, - ) - as EthTokenWallet; + ref.read(tokenServiceStateProvider.state).state = Wallet.loadTokenWallet( + ethWallet: + ref.read(pWallets).getWallet(widget.walletId) as EthereumWallet, + contract: widget.token, + ) as EthTokenWallet; final success = await showLoading( whileFuture: _loadTokenWallet(context, ref), @@ -179,13 +176,12 @@ class _MyTokenSelectItemState extends ConsumerState { Text( widget.token.name, style: isDesktop - ? STextStyles.desktopTextExtraSmall( - context, - ).copyWith( - color: Theme.of( - context, - ).extension()!.textDark, - ) + ? STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ) : STextStyles.titleBold12(context), ), const Spacer(), @@ -208,13 +204,12 @@ class _MyTokenSelectItemState extends ConsumerState { tokenContract: widget.token, ), style: isDesktop - ? STextStyles.desktopTextExtraSmall( - context, - ).copyWith( - color: Theme.of( - context, - ).extension()!.textDark, - ) + ? STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ) : STextStyles.itemSubtitle(context), ), ], diff --git a/lib/pages/token_view/sub_widgets/token_summary.dart b/lib/pages/token_view/sub_widgets/token_summary.dart index 0f1bd177d4..4119b38501 100644 --- a/lib/pages/token_view/sub_widgets/token_summary.dart +++ b/lib/pages/token_view/sub_widgets/token_summary.dart @@ -83,9 +83,9 @@ class TokenSummary extends ConsumerWidget { children: [ SvgPicture.asset( Assets.svg.walletDesktop, - color: Theme.of( - context, - ).extension()!.tokenSummaryTextSecondary, + color: Theme.of(context) + .extension()! + .tokenSummaryTextSecondary, width: 12, height: 12, ), @@ -93,9 +93,9 @@ class TokenSummary extends ConsumerWidget { Text( ref.watch(pWalletName(walletId)), style: STextStyles.w500_12(context).copyWith( - color: Theme.of( - context, - ).extension()!.tokenSummaryTextSecondary, + color: Theme.of(context) + .extension()! + .tokenSummaryTextSecondary, ), ), ], @@ -113,9 +113,9 @@ class TokenSummary extends ConsumerWidget { ) .format(balance.total, tokenContract: token), style: STextStyles.pageTitleH1(context).copyWith( - color: Theme.of( - context, - ).extension()!.tokenSummaryTextPrimary, + color: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, ), ), const SizedBox(width: 10), @@ -131,9 +131,9 @@ class TokenSummary extends ConsumerWidget { Text( "${(balance.total.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", style: STextStyles.subtitle500(context).copyWith( - color: Theme.of( - context, - ).extension()!.tokenSummaryTextPrimary, + color: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, ), ), const SizedBox(height: 20), @@ -152,9 +152,9 @@ class TokenSummary extends ConsumerWidget { (value) => value!.tokenContract.address, ), ), - overrideIconColor: Theme.of( - context, - ).extension()!.topNavIconPrimary, + overrideIconColor: Theme.of(context) + .extension()! + .topNavIconPrimary, ), ), ], @@ -263,9 +263,9 @@ class TokenOptionsButton extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ RawMaterialButton( - fillColor: Theme.of( - context, - ).extension()!.tokenSummaryButtonBG, + fillColor: Theme.of(context) + .extension()! + .tokenSummaryButtonBG, elevation: 0, focusElevation: 0, hoverElevation: 0, @@ -283,26 +283,27 @@ class TokenOptionsButton extends StatelessWidget { condition: iconSize < 24, builder: (child) => RoundedContainer( padding: const EdgeInsets.all(6), - color: Theme.of( - context, - ).extension()!.tokenSummaryIcon.withOpacity(0.4), + color: Theme.of(context) + .extension()! + .tokenSummaryIcon + .withOpacity(0.4), radiusMultiplier: 10, child: Center(child: child), ), child: iconAssetPathSVG.startsWith("assets/") ? SvgPicture.asset( iconAssetPathSVG, - color: Theme.of( - context, - ).extension()!.tokenSummaryIcon, + color: Theme.of(context) + .extension()! + .tokenSummaryIcon, width: iconSize, height: iconSize, ) : SvgPicture.file( File(iconAssetPathSVG), - color: Theme.of( - context, - ).extension()!.tokenSummaryIcon, + color: Theme.of(context) + .extension()! + .tokenSummaryIcon, width: iconSize, height: iconSize, ), @@ -313,9 +314,9 @@ class TokenOptionsButton extends StatelessWidget { Text( subLabel, style: STextStyles.w500_12(context).copyWith( - color: Theme.of( - context, - ).extension()!.tokenSummaryTextPrimary, + color: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, ), ), ], diff --git a/lib/pages/token_view/sub_widgets/token_summary_sol.dart b/lib/pages/token_view/sub_widgets/token_summary_sol.dart index 9d627c1f58..76d1cc7771 100644 --- a/lib/pages/token_view/sub_widgets/token_summary_sol.dart +++ b/lib/pages/token_view/sub_widgets/token_summary_sol.dart @@ -63,8 +63,9 @@ class SolanaTokenSummary extends ConsumerWidget { child: Text( "Loading token data...", style: STextStyles.subtitle500(context).copyWith( - color: - Theme.of(context).extension()!.tokenSummaryTextPrimary, + color: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, ), ), ), @@ -73,12 +74,7 @@ class SolanaTokenSummary extends ConsumerWidget { // Watch the balance from the database provider. final balance = ref.watch( - pSolanaTokenBalance( - ( - walletId: walletId, - tokenMint: tokenMint, - ), - ), + pSolanaTokenBalance((walletId: walletId, tokenMint: tokenMint)), ); Decimal? price; @@ -103,9 +99,9 @@ class SolanaTokenSummary extends ConsumerWidget { children: [ SvgPicture.asset( Assets.svg.walletDesktop, - color: Theme.of( - context, - ).extension()!.tokenSummaryTextSecondary, + color: Theme.of(context) + .extension()! + .tokenSummaryTextSecondary, width: 12, height: 12, ), @@ -113,9 +109,9 @@ class SolanaTokenSummary extends ConsumerWidget { Text( ref.watch(pWalletName(walletId)), style: STextStyles.w500_12(context).copyWith( - color: Theme.of( - context, - ).extension()!.tokenSummaryTextSecondary, + color: Theme.of(context) + .extension()! + .tokenSummaryTextSecondary, ), ), ], @@ -125,17 +121,17 @@ class SolanaTokenSummary extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - balance.total.decimal.toStringAsFixed(tokenWallet.tokenDecimals), + balance.total.decimal.toStringAsFixed( + tokenWallet.tokenDecimals, + ), style: STextStyles.pageTitleH1(context).copyWith( - color: Theme.of( - context, - ).extension()!.tokenSummaryTextPrimary, + color: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, ), ), const SizedBox(width: 10), - CoinTickerTag( - ticker: tokenWallet.tokenSymbol, - ), + CoinTickerTag(ticker: tokenWallet.tokenSymbol), ], ), if (price != null) const SizedBox(height: 6), @@ -143,9 +139,9 @@ class SolanaTokenSummary extends ConsumerWidget { Text( "${(balance.total.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", style: STextStyles.subtitle500(context).copyWith( - color: Theme.of( - context, - ).extension()!.tokenSummaryTextPrimary, + color: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, ), ), const SizedBox(height: 20), @@ -163,8 +159,9 @@ class SolanaTokenSummary extends ConsumerWidget { walletId: walletId, initialSyncStatus: initialSyncStatus, tokenContractAddress: tokenMint, - overrideIconColor: - Theme.of(context).extension()!.topNavIconPrimary, + overrideIconColor: Theme.of(context) + .extension()! + .topNavIconPrimary, ), ), ], @@ -250,8 +247,9 @@ class TokenOptionsButton extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ RawMaterialButton( - fillColor: - Theme.of(context).extension()!.tokenSummaryButtonBG, + fillColor: Theme.of(context) + .extension()! + .tokenSummaryButtonBG, elevation: 0, focusElevation: 0, hoverElevation: 0, @@ -267,36 +265,32 @@ class TokenOptionsButton extends StatelessWidget { padding: const EdgeInsets.all(10), child: ConditionalParent( condition: iconSize < 24, - builder: - (child) => RoundedContainer( - padding: const EdgeInsets.all(6), - color: Theme.of(context) - .extension()! - .tokenSummaryIcon - .withOpacity(0.4), - radiusMultiplier: 10, - child: Center(child: child), - ), - child: - iconAssetPathSVG.startsWith("assets/") - ? SvgPicture.asset( - iconAssetPathSVG, - color: - Theme.of( - context, - ).extension()!.tokenSummaryIcon, - width: iconSize, - height: iconSize, - ) - : SvgPicture.file( - File(iconAssetPathSVG), - color: - Theme.of( - context, - ).extension()!.tokenSummaryIcon, - width: iconSize, - height: iconSize, - ), + builder: (child) => RoundedContainer( + padding: const EdgeInsets.all(6), + color: Theme.of(context) + .extension()! + .tokenSummaryIcon + .withOpacity(0.4), + radiusMultiplier: 10, + child: Center(child: child), + ), + child: iconAssetPathSVG.startsWith("assets/") + ? SvgPicture.asset( + iconAssetPathSVG, + color: Theme.of(context) + .extension()! + .tokenSummaryIcon, + width: iconSize, + height: iconSize, + ) + : SvgPicture.file( + File(iconAssetPathSVG), + color: Theme.of(context) + .extension()! + .tokenSummaryIcon, + width: iconSize, + height: iconSize, + ), ), ), ), @@ -304,10 +298,9 @@ class TokenOptionsButton extends StatelessWidget { Text( subLabel, style: STextStyles.w500_12(context).copyWith( - color: - Theme.of( - context, - ).extension()!.tokenSummaryTextPrimary, + color: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, ), ), ], diff --git a/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart b/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart index 120b99b258..8e4e22a631 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart @@ -44,23 +44,21 @@ class WalletBalanceToggleSheet extends ConsumerWidget { _BalanceType _bal = ref.watch(walletBalanceToggleStateProvider.state).state == - WalletBalanceToggleState.available - ? _BalanceType.available - : _BalanceType.full; + WalletBalanceToggleState.available + ? _BalanceType.available + : _BalanceType.full; Balance? balancePrivate; if (hasPrivate) { - balancePrivate = - isMweb - ? ref.watch(pWalletBalanceSecondary(walletId)) - : ref.watch(pWalletBalanceTertiary(walletId)); + balancePrivate = isMweb + ? ref.watch(pWalletBalanceSecondary(walletId)) + : ref.watch(pWalletBalanceTertiary(walletId)); if (ref.watch(publicPrivateBalanceStateProvider.state).state == BalanceType.private) { - _bal = - _bal == _BalanceType.available - ? _BalanceType.privateAvailable - : _BalanceType.privateFull; + _bal = _bal == _BalanceType.available + ? _BalanceType.privateAvailable + : _BalanceType.privateFull; } } @@ -85,10 +83,9 @@ class WalletBalanceToggleSheet extends ConsumerWidget { Center( child: Container( decoration: BoxDecoration( - color: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, ), @@ -244,10 +241,9 @@ class BalanceSelector extends ConsumerWidget { width: 20, height: 20, child: Radio( - activeColor: - Theme.of( - context, - ).extension()!.radioButtonIconEnabled, + activeColor: Theme.of(context) + .extension()! + .radioButtonIconEnabled, value: value, groupValue: groupValue, onChanged: onChanged, @@ -262,10 +258,9 @@ class BalanceSelector extends ConsumerWidget { Text( ref.watch(pAmountFormatter(coin)).format(balance), style: STextStyles.itemSubtitle12(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textSubtitle1, + color: Theme.of(context) + .extension()! + .textSubtitle1, ), ), ], diff --git a/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart b/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart index 68e0123bc8..eccb143847 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart @@ -101,10 +101,9 @@ class WalletSummaryInfo extends ConsumerWidget { "${_showAvailable ? "Available" : "Full"} ${type.name.capitalize()} balance"; switch (type) { case BalanceType.private: - final balance = - coin is Firo - ? ref.watch(pWalletBalanceTertiary(walletId)) - : ref.watch(pWalletBalanceSecondary(walletId)); + final balance = coin is Firo + ? ref.watch(pWalletBalanceTertiary(walletId)) + : ref.watch(pWalletBalanceSecondary(walletId)); balanceToShow = _showAvailable ? balance.spendable : balance.total; break; @@ -122,23 +121,21 @@ class WalletSummaryInfo extends ConsumerWidget { List? imageBytes; if (coin is Banano) { - imageBytes = - (ref.watch(pWallets).getWallet(walletId) as BananoWallet) - .getMonkeyImageBytes(); + imageBytes = (ref.watch(pWallets).getWallet(walletId) as BananoWallet) + .getMonkeyImageBytes(); } return ConditionalParent( condition: imageBytes != null, - builder: - (child) => Stack( - children: [ - Positioned.fill( - left: 150.0, - child: SvgPicture.memory(Uint8List.fromList(imageBytes!)), - ), - child, - ], + builder: (child) => Stack( + children: [ + Positioned.fill( + left: 150.0, + child: SvgPicture.memory(Uint8List.fromList(imageBytes!)), ), + child, + ], + ), child: Row( children: [ Expanded( @@ -151,12 +148,14 @@ class WalletSummaryInfo extends ConsumerWidget { if (ref.read(walletBalanceToggleStateProvider) == WalletBalanceToggleState.available) { ref - .read(walletBalanceToggleStateProvider.notifier) - .state = WalletBalanceToggleState.full; + .read(walletBalanceToggleStateProvider.notifier) + .state = + WalletBalanceToggleState.full; } else { ref - .read(walletBalanceToggleStateProvider.notifier) - .state = WalletBalanceToggleState.available; + .read(walletBalanceToggleStateProvider.notifier) + .state = + WalletBalanceToggleState.available; } } else { showSheet(context); @@ -167,20 +166,18 @@ class WalletSummaryInfo extends ConsumerWidget { Text( title, style: STextStyles.subtitle500(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textFavoriteCard, + color: Theme.of(context) + .extension()! + .textFavoriteCard, ), ), if (!toggleBalance) ...[ const SizedBox(width: 4), SvgPicture.asset( Assets.svg.chevronDown, - color: - Theme.of( - context, - ).extension()!.textFavoriteCard, + color: Theme.of(context) + .extension()! + .textFavoriteCard, width: 8, height: 4, ), @@ -210,10 +207,9 @@ class WalletSummaryInfo extends ConsumerWidget { ref.watch(pAmountFormatter(coin)).format(balanceToShow), style: STextStyles.pageTitleH1(context).copyWith( fontSize: 24, - color: - Theme.of( - context, - ).extension()!.textFavoriteCard, + color: Theme.of(context) + .extension()! + .textFavoriteCard, ), ), ), @@ -221,10 +217,9 @@ class WalletSummaryInfo extends ConsumerWidget { Text( "${(price.value * balanceToShow.decimal).toAmount(fractionDigits: 2).fiatString(locale: locale)} $baseCurrency", style: STextStyles.subtitle500(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textFavoriteCard, + color: Theme.of(context) + .extension()! + .textFavoriteCard, ), ), ], diff --git a/lib/pages/wallets_view/sub_widgets/favorite_card.dart b/lib/pages/wallets_view/sub_widgets/favorite_card.dart index aad2dc5dbb..3a0df66ebb 100644 --- a/lib/pages/wallets_view/sub_widgets/favorite_card.dart +++ b/lib/pages/wallets_view/sub_widgets/favorite_card.dart @@ -78,53 +78,51 @@ class _FavoriteCardState extends ConsumerState { } return ConditionalParent( condition: Util.isDesktop, - builder: - (child) => MouseRegion( - cursor: SystemMouseCursors.click, - onEnter: (_) { - setState(() { - _hovering = true; - }); - }, - onExit: (_) { - setState(() { - _hovering = false; - }); - }, - child: AnimatedScale( - duration: const Duration(milliseconds: 200), - scale: _hovering ? 1.05 : 1, - child: AnimatedContainer( - duration: const Duration(milliseconds: 200), - decoration: - _hovering - ? BoxDecoration( - color: Colors.transparent, - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - boxShadow: [ - Theme.of( - context, - ).extension()!.standardBoxShadow, - Theme.of( - context, - ).extension()!.standardBoxShadow, - Theme.of( - context, - ).extension()!.standardBoxShadow, - ], - ) - : BoxDecoration( - color: Colors.transparent, - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - ), - child: child, - ), - ), + builder: (child) => MouseRegion( + cursor: SystemMouseCursors.click, + onEnter: (_) { + setState(() { + _hovering = true; + }); + }, + onExit: (_) { + setState(() { + _hovering = false; + }); + }, + child: AnimatedScale( + duration: const Duration(milliseconds: 200), + scale: _hovering ? 1.05 : 1, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + decoration: _hovering + ? BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + boxShadow: [ + Theme.of(context) + .extension()! + .standardBoxShadow, + Theme.of(context) + .extension()! + .standardBoxShadow, + Theme.of(context) + .extension()! + .standardBoxShadow, + ], + ) + : BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + child: child, ), + ), + ), child: GestureDetector( onTap: () async { final wallet = ref.read(pWallets).getWallet(walletId); @@ -159,13 +157,11 @@ class _FavoriteCardState extends ConsumerState { if (mounted) { if (Util.isDesktop) { - await Navigator.of( - context, - ).pushNamed(DesktopWalletView.routeName, arguments: walletId); + await Navigator.of(context) + .pushNamed(DesktopWalletView.routeName, arguments: walletId); } else { - await Navigator.of( - context, - ).pushNamed(WalletView.routeName, arguments: walletId); + await Navigator.of(context) + .pushNamed(WalletView.routeName, arguments: walletId); } } }, @@ -193,10 +189,9 @@ class _FavoriteCardState extends ConsumerState { child: Text( ref.watch(pWalletName(walletId)), style: STextStyles.itemSubtitle12(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textFavoriteCard, + color: Theme.of(context) + .extension()! + .textFavoriteCard, ), overflow: TextOverflow.fade, ), @@ -215,15 +210,18 @@ class _FavoriteCardState extends ConsumerState { Amount total = balance.total; if (coin is Firo) { - total += - ref.watch(pWalletBalanceSecondary(walletId)).total; - total += - ref.watch(pWalletBalanceTertiary(walletId)).total; + total += ref + .watch(pWalletBalanceSecondary(walletId)) + .total; + total += ref + .watch(pWalletBalanceTertiary(walletId)) + .total; } else if (ref.watch( pWalletInfo(walletId).select((s) => s.isMwebEnabled), )) { - total += - ref.watch(pWalletBalanceSecondary(walletId)).total; + total += ref + .watch(pWalletBalanceSecondary(walletId)) + .total; } Amount fiatTotal = Amount.zero; @@ -245,10 +243,9 @@ class _FavoriteCardState extends ConsumerState { ref.watch(pAmountFormatter(coin)).format(total), style: STextStyles.titleBold12(context).copyWith( fontSize: 16, - color: - Theme.of(context) - .extension()! - .textFavoriteCard, + color: Theme.of(context) + .extension()! + .textFavoriteCard, ), ), ), @@ -257,15 +254,13 @@ class _FavoriteCardState extends ConsumerState { if (externalCalls && price != null) Text( "${fiatTotal.fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", - style: STextStyles.itemSubtitle12( - context, - ).copyWith( - fontSize: 10, - color: - Theme.of(context) + style: STextStyles.itemSubtitle12(context) + .copyWith( + fontSize: 10, + color: Theme.of(context) .extension()! .textFavoriteCard, - ), + ), ), ], ); diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart index ac01104ff3..46ec1bcfe3 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart @@ -182,9 +182,9 @@ class _WDesktopWalletSummaryState extends ConsumerState { SelectableText( "${Amount.fromDecimal(price.value * balanceToShow.decimal, fractionDigits: 2).fiatString(locale: locale)} $baseCurrency", style: STextStyles.desktopTextExtraSmall(context).copyWith( - color: Theme.of( - context, - ).extension()!.textSubtitle1, + color: Theme.of(context) + .extension()! + .textSubtitle1, ), ), // if (coin is Firo) diff --git a/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/appearance_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/appearance_settings.dart index 8fa05124b1..711628e37c 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/appearance_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/appearance_settings.dart @@ -14,6 +14,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../../../../app_config.dart'; import '../../../../models/isar/stack_theme.dart'; import 'sub_widgets/desktop_manage_themes.dart'; @@ -27,6 +28,7 @@ import '../../../../utilities/constants.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; import '../../../../widgets/rounded_white_container.dart'; + import 'package:tuple/tuple.dart'; class AppearanceOptionSettings extends ConsumerStatefulWidget { @@ -48,9 +50,7 @@ class _AppearanceOptionSettings child: Column( children: [ Padding( - padding: const EdgeInsets.only( - right: 30, - ), + padding: const EdgeInsets.only(right: 30), child: RoundedWhiteContainer( radiusMultiplier: 2, child: Wrap( @@ -77,16 +77,17 @@ class _AppearanceOptionSettings children: [ TextSpan( text: "Appearances", - style: - STextStyles.desktopTextSmall(context), + style: STextStyles.desktopTextSmall( + context, + ), ), TextSpan( text: "\n\nCustomize how your ${AppConfig.appName} looks according to your preferences.", style: STextStyles.desktopTextExtraExtraSmall( - context, - ), + context, + ), ), ], ), @@ -96,9 +97,7 @@ class _AppearanceOptionSettings ), const Padding( padding: EdgeInsets.all(10.0), - child: Divider( - thickness: 0.5, - ), + child: Divider(thickness: 0.5), ), Padding( padding: const EdgeInsets.all(10.0), @@ -109,10 +108,10 @@ class _AppearanceOptionSettings "Display favorite wallets", style: STextStyles.desktopTextExtraSmall(context) .copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ), + color: Theme.of(context) + .extension()! + .textDark, + ), textAlign: TextAlign.left, ), SizedBox( @@ -126,8 +125,9 @@ class _AppearanceOptionSettings ), onValueChanged: (newValue) { ref - .read(prefsChangeNotifierProvider) - .showFavoriteWallets = newValue; + .read(prefsChangeNotifierProvider) + .showFavoriteWallets = + newValue; }, ), ), @@ -136,9 +136,7 @@ class _AppearanceOptionSettings ), const Padding( padding: EdgeInsets.all(10.0), - child: Divider( - thickness: 0.5, - ), + child: Divider(thickness: 0.5), ), Padding( padding: const EdgeInsets.all(10.0), @@ -149,10 +147,10 @@ class _AppearanceOptionSettings "Choose theme", style: STextStyles.desktopTextExtraSmall(context) .copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ), + color: Theme.of(context) + .extension()! + .textDark, + ), textAlign: TextAlign.left, ), ], @@ -175,9 +173,7 @@ class _AppearanceOptionSettings } class ThemeToggle extends ConsumerStatefulWidget { - const ThemeToggle({ - super.key, - }); + const ThemeToggle({super.key}); @override ConsumerState createState() => _ThemeToggle(); @@ -219,8 +215,9 @@ class _ThemeToggle extends ConsumerState { } // apply theme - ref.read(themeProvider.notifier).state = - ref.read(pThemeService).getTheme(themeId: themeId)!; + ref.read(themeProvider.notifier).state = ref + .read(pThemeService) + .getTheme(themeId: themeId)!; // Assets.precache(context); } else { @@ -239,8 +236,9 @@ class _ThemeToggle extends ConsumerState { ref.read(prefsChangeNotifierProvider.notifier).themeId = themeId; // apply theme - ref.read(themeProvider.notifier).state = - ref.read(pThemeService).getTheme(themeId: themeId)!; + ref.read(themeProvider.notifier).state = ref + .read(pThemeService) + .getTheme(themeId: themeId)!; // Assets.precache(context); } @@ -278,16 +276,20 @@ class _ThemeToggle extends ConsumerState { void initState() { _updateInstalledList(); - _subscription = - ref.read(mainDBProvider).isar.stackThemes.watchLazy().listen((_) { - if (mounted) { - WidgetsBinding.instance.addPostFrameCallback((_) { - setState(() { - _updateInstalledList(); - }); + _subscription = ref + .read(mainDBProvider) + .isar + .stackThemes + .watchLazy() + .listen((_) { + if (mounted) { + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + _updateInstalledList(); + }); + }); + } }); - } - }); super.initState(); } @@ -328,27 +330,23 @@ class _ThemeToggle extends ConsumerState { width: 2.5, color: _current == i ? Theme.of(context) - .extension()! - .infoItemIcons + .extension()! + .infoItemIcons : Theme.of(context) - .extension()! - .popupBG, + .extension()! + .popupBG, ), borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, ), ), child: SvgPicture.file( - File( - installedThemeIdNames[i].item3, - ), + File(installedThemeIdNames[i].item3), height: 160, width: 200, ), ), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), Row( children: [ SizedBox( @@ -367,17 +365,15 @@ class _ThemeToggle extends ConsumerState { }, ), ), - const SizedBox( - width: 14, - ), + const SizedBox(width: 14), Text( installedThemeIdNames[i].item2, style: STextStyles.desktopTextExtraSmall(context) .copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ), + color: Theme.of(context) + .extension()! + .textDark, + ), ), ], ), diff --git a/lib/providers/global/prefs_provider.dart b/lib/providers/global/prefs_provider.dart index abe6653d20..fdcac56dab 100644 --- a/lib/providers/global/prefs_provider.dart +++ b/lib/providers/global/prefs_provider.dart @@ -10,6 +10,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../utilities/prefs.dart'; int _count = 0; diff --git a/lib/utilities/prefs.dart b/lib/utilities/prefs.dart index 56013d64d7..36677bd6d6 100644 --- a/lib/utilities/prefs.dart +++ b/lib/utilities/prefs.dart @@ -108,10 +108,9 @@ class Prefs extends ChangeNotifier { Future _getLastUnlockedTimeout() async { return (DB.instance.get( - boxName: DB.boxNamePrefs, - key: "lastUnlockedTimeout", - )) - as int? ?? + boxName: DB.boxNamePrefs, + key: "lastUnlockedTimeout", + )) as int? ?? 60; } @@ -135,10 +134,9 @@ class Prefs extends ChangeNotifier { Future _getLastUnlocked() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "lastUnlocked", - ) - as int? ?? + boxName: DB.boxNamePrefs, + key: "lastUnlocked", + ) as int? ?? 0; } @@ -171,10 +169,9 @@ class Prefs extends ChangeNotifier { Future _getCurrentNotificationIndex() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "currentNotificationId", - ) - as int? ?? + boxName: DB.boxNamePrefs, + key: "currentNotificationId", + ) as int? ?? 0; } @@ -199,10 +196,9 @@ class Prefs extends ChangeNotifier { Future> _getWalletIdsSyncOnStartup() async { final list = await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "walletIdsSyncOnStartup", - ) - as List? ?? + boxName: DB.boxNamePrefs, + key: "walletIdsSyncOnStartup", + ) as List? ?? []; return List.from(list); } @@ -228,10 +224,9 @@ class Prefs extends ChangeNotifier { Future _getSyncType() async { final int index = await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "syncTypeIndex", - ) - as int? ?? + boxName: DB.boxNamePrefs, + key: "syncTypeIndex", + ) as int? ?? SyncingType.allWalletsOnStartup.index; return SyncingType.values[index]; } @@ -256,10 +251,9 @@ class Prefs extends ChangeNotifier { Future _getUseWifiOnly() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "wifiOnly", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "wifiOnly", + ) as bool? ?? false; } @@ -283,10 +277,9 @@ class Prefs extends ChangeNotifier { Future _getShowFavoriteWallets() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "showFavoriteWallets", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "showFavoriteWallets", + ) as bool? ?? true; } @@ -310,10 +303,9 @@ class Prefs extends ChangeNotifier { Future _getPreferredLanguage() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "language", - ) - as String? ?? + boxName: DB.boxNamePrefs, + key: "language", + ) as String? ?? Language.englishUS.description; } @@ -337,10 +329,9 @@ class Prefs extends ChangeNotifier { Future _getPreferredCurrency() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "currency", - ) - as String? ?? + boxName: DB.boxNamePrefs, + key: "currency", + ) as String? ?? "USD"; } @@ -405,10 +396,9 @@ class Prefs extends ChangeNotifier { Future _getRandomizePIN() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "randomizePIN", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "randomizePIN", + ) as bool? ?? false; } @@ -432,10 +422,9 @@ class Prefs extends ChangeNotifier { Future _getUseBiometrics() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "useBiometrics", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "useBiometrics", + ) as bool? ?? false; } @@ -459,10 +448,9 @@ class Prefs extends ChangeNotifier { Future _getHasPin() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "hasPin", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "hasPin", + ) as bool? ?? false; } @@ -486,10 +474,9 @@ class Prefs extends ChangeNotifier { Future _getHasDuressPin() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "hasDuressPin", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "hasDuressPin", + ) as bool? ?? false; } @@ -513,10 +500,9 @@ class Prefs extends ChangeNotifier { Future _getBiometricsDuress() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "biometricsDuress", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "biometricsDuress", + ) as bool? ?? false; } @@ -540,10 +526,9 @@ class Prefs extends ChangeNotifier { Future _getHasFamiliarity() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "familiarity", - ) - as int? ?? + boxName: DB.boxNamePrefs, + key: "familiarity", + ) as int? ?? 0; } @@ -567,10 +552,9 @@ class Prefs extends ChangeNotifier { Future _getTorKillswitch() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "torKillswitch", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "torKillswitch", + ) as bool? ?? true; } @@ -594,10 +578,9 @@ class Prefs extends ChangeNotifier { Future _getShowTestNetCoins() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "showTestNetCoins", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "showTestNetCoins", + ) as bool? ?? false; } @@ -624,10 +607,9 @@ class Prefs extends ChangeNotifier { Future _getIsAutoBackupEnabled() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "isAutoBackupEnabled", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "isAutoBackupEnabled", + ) as bool? ?? false; } @@ -651,10 +633,9 @@ class Prefs extends ChangeNotifier { Future _getAutoBackupLocation() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "autoBackupLocation", - ) - as String?; + boxName: DB.boxNamePrefs, + key: "autoBackupLocation", + ) as String?; } // auto backup frequency type @@ -695,12 +676,10 @@ class Prefs extends ChangeNotifier { } Future _getBackupFrequencyType() async { - String? rate = - await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "backupFrequencyType", - ) - as String?; + String? rate = await DB.instance.get( + boxName: DB.boxNamePrefs, + key: "backupFrequencyType", + ) as String?; rate ??= "10Min"; switch (rate) { case "10Min": @@ -734,10 +713,9 @@ class Prefs extends ChangeNotifier { Future _getLastAutoBackup() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "lastAutoBackup", - ) - as DateTime?; + boxName: DB.boxNamePrefs, + key: "lastAutoBackup", + ) as DateTime?; } // auto backup @@ -763,10 +741,9 @@ class Prefs extends ChangeNotifier { Future _getHideBlockExplorerWarning() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "hideBlockExplorerWarning", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "hideBlockExplorerWarning", + ) as bool? ?? false; } @@ -793,10 +770,9 @@ class Prefs extends ChangeNotifier { Future _getGotoWalletOnStartup() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "gotoWalletOnStartup", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "gotoWalletOnStartup", + ) as bool? ?? false; } @@ -820,10 +796,9 @@ class Prefs extends ChangeNotifier { Future _getStartupWalletId() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "startupWalletId", - ) - as String?; + boxName: DB.boxNamePrefs, + key: "startupWalletId", + ) as String?; } // incognito mode off by default @@ -849,10 +824,9 @@ class Prefs extends ChangeNotifier { Future _getHasExternalCalls() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "externalCalls", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "externalCalls", + ) as bool? ?? true; } @@ -871,9 +845,10 @@ class Prefs extends ChangeNotifier { String? get userID => _userId; Future _getUserId() async { - String? userID = - await DB.instance.get(boxName: DB.boxNamePrefs, key: "userID") - as String?; + String? userID = await DB.instance.get( + boxName: DB.boxNamePrefs, + key: "userID", + ) as String?; if (userID == null) { userID = const Uuid().v4(); await saveUserID(userID); @@ -895,12 +870,10 @@ class Prefs extends ChangeNotifier { int? get signupEpoch => _signupEpoch; Future _getSignupEpoch() async { - int? signupEpoch = - await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "signupEpoch", - ) - as int?; + int? signupEpoch = await DB.instance.get( + boxName: DB.boxNamePrefs, + key: "signupEpoch", + ) as int?; if (signupEpoch == null) { signupEpoch = DateTime.now().millisecondsSinceEpoch ~/ @@ -940,10 +913,9 @@ class Prefs extends ChangeNotifier { Future _getEnableCoinControl() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "enableCoinControl", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "enableCoinControl", + ) as bool? ?? false; } @@ -967,10 +939,9 @@ class Prefs extends ChangeNotifier { Future _getEnableSystemBrightness() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "enableSystemBrightness", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "enableSystemBrightness", + ) as bool? ?? false; } @@ -994,10 +965,9 @@ class Prefs extends ChangeNotifier { Future _getThemeId() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "themeId", - ) - as String? ?? + boxName: DB.boxNamePrefs, + key: "themeId", + ) as String? ?? "light"; } @@ -1021,10 +991,9 @@ class Prefs extends ChangeNotifier { Future _getSystemBrightnessLightThemeId() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "systemBrightnessLightThemeId", - ) - as String? ?? + boxName: DB.boxNamePrefs, + key: "systemBrightnessLightThemeId", + ) as String? ?? "light"; } @@ -1048,10 +1017,9 @@ class Prefs extends ChangeNotifier { Future _getSystemBrightnessDarkTheme() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "systemBrightnessDarkThemeId", - ) - as String? ?? + boxName: DB.boxNamePrefs, + key: "systemBrightnessDarkThemeId", + ) as String? ?? "dark"; } @@ -1081,10 +1049,9 @@ class Prefs extends ChangeNotifier { for (final coin in AppConfig.coins) { final unitIndex = await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "amountUnitFor${coin.identifier}", - ) - as int? ?? + boxName: DB.boxNamePrefs, + key: "amountUnitFor${coin.identifier}", + ) as int? ?? 0; // 0 is "normal" _amountUnits[coin] = AmountUnit.values[unitIndex]; } @@ -1116,10 +1083,9 @@ class Prefs extends ChangeNotifier { for (final coin in AppConfig.coins) { final decimals = await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "maxDecimalsFor${coin.identifier}", - ) - as int? ?? + boxName: DB.boxNamePrefs, + key: "maxDecimalsFor${coin.identifier}", + ) as int? ?? (coin.fractionDigits > 18 ? 18 : coin.fractionDigits); // use some sane max rather than up to 30 that nano uses _amountDecimals[coin.identifier] = decimals; @@ -1152,10 +1118,9 @@ class Prefs extends ChangeNotifier { Future _getUseTor() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "useTor", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "useTor", + ) as bool? ?? false; } @@ -1184,30 +1149,25 @@ class Prefs extends ChangeNotifier { } Future> _getFusionServerInfo() async { - final map = - await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "fusionServerInfoMap", - ) - as Map?; + final map = await DB.instance.get( + boxName: DB.boxNamePrefs, + key: "fusionServerInfoMap", + ) as Map?; if (map == null) { return _fusionServerInfo; } - final actualMap = Map.from( - map, - ).map((key, value) => MapEntry(key, FusionInfo.fromJsonString(value))); + final actualMap = Map.from(map) + .map((key, value) => MapEntry(key, FusionInfo.fromJsonString(value))); // legacy bch check if (actualMap["bitcoincash"] == null || actualMap["bitcoincashTestnet"] == null) { - final saved = - await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "fusionServerInfo", - ) - as String?; + final saved = await DB.instance.get( + boxName: DB.boxNamePrefs, + key: "fusionServerInfo", + ) as String?; if (saved != null) { final bchInfo = FusionInfo.fromJsonString(saved); @@ -1248,10 +1208,9 @@ class Prefs extends ChangeNotifier { Future _getAutoPin() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "autoPin", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "autoPin", + ) as bool? ?? false; } @@ -1275,10 +1234,9 @@ class Prefs extends ChangeNotifier { Future _getEnableExchange() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "showExchange", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "showExchange", + ) as bool? ?? true; } @@ -1299,10 +1257,9 @@ class Prefs extends ChangeNotifier { Future _getAdvancedFiroFeatures() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "advancedFiroFeatures", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "advancedFiroFeatures", + ) as bool? ?? false; } @@ -1323,10 +1280,9 @@ class Prefs extends ChangeNotifier { Future _getLogsPath() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "logsPath", - ) - as String?; + boxName: DB.boxNamePrefs, + key: "logsPath", + ) as String?; } // log level pref @@ -1345,12 +1301,10 @@ class Prefs extends ChangeNotifier { } Future _getLogLevel() async { - final value = - await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "logLevel", - ) - as int?; + final value = await DB.instance.get( + boxName: DB.boxNamePrefs, + key: "logLevel", + ) as int?; try { return Level.values.firstWhere((e) => e.value == value); @@ -1384,10 +1338,9 @@ class Prefs extends ChangeNotifier { Future _getAutoLockInfo() async { final map = await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "autoLockInfo", - ) - as Map? ?? + boxName: DB.boxNamePrefs, + key: "autoLockInfo", + ) as Map? ?? {"enabled": false, "minutes": 10}; return (enabled: map["enabled"] as bool, minutes: map["minutes"] as int); @@ -1410,10 +1363,9 @@ class Prefs extends ChangeNotifier { Future _getPrivacyScreen() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "privacyScreen", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "privacyScreen", + ) as bool? ?? false; } @@ -1434,10 +1386,9 @@ class Prefs extends ChangeNotifier { Future _getDisableScreenShots() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "disableScreenShots", - ) - as bool? ?? + boxName: DB.boxNamePrefs, + key: "disableScreenShots", + ) as bool? ?? false; } } diff --git a/lib/widgets/managed_favorite.dart b/lib/widgets/managed_favorite.dart index c89bc483b2..f1e66683c3 100644 --- a/lib/widgets/managed_favorite.dart +++ b/lib/widgets/managed_favorite.dart @@ -13,6 +13,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; + import '../providers/db/main_db_provider.dart'; import '../themes/coin_icon_provider.dart'; import '../themes/stack_colors.dart'; @@ -28,10 +29,7 @@ import 'custom_buttons/favorite_toggle.dart'; import 'rounded_white_container.dart'; class ManagedFavorite extends ConsumerStatefulWidget { - const ManagedFavorite({ - super.key, - required this.walletId, - }); + const ManagedFavorite({super.key, required this.walletId}); final String walletId; @@ -54,7 +52,7 @@ class _ManagedFavoriteCardState extends ConsumerState { if (coin is Firo) { final balancePrivate = ref.watch(pWalletBalanceSecondary(walletId)).total + - ref.watch(pWalletBalanceTertiary(walletId)).total; + ref.watch(pWalletBalanceTertiary(walletId)).total; total += balancePrivate; } @@ -65,7 +63,9 @@ class _ManagedFavoriteCardState extends ConsumerState { padding: EdgeInsets.all(isDesktop ? 0 : 4.0), child: RawMaterialButton( onPressed: () { - ref.read(pWalletInfo(walletId)).updateIsFavourite( + ref + .read(pWalletInfo(walletId)) + .updateIsFavourite( !isFavourite, isar: ref.read(mainDBProvider).isar, ); @@ -77,10 +77,7 @@ class _ManagedFavoriteCardState extends ConsumerState { ), child: Padding( padding: isDesktop - ? const EdgeInsets.symmetric( - horizontal: 20, - vertical: 12, - ) + ? const EdgeInsets.symmetric(horizontal: 20, vertical: 12) : const EdgeInsets.all(8), child: Row( children: [ @@ -94,17 +91,13 @@ class _ManagedFavoriteCardState extends ConsumerState { child: Padding( padding: EdgeInsets.all(isDesktop ? 6 : 4), child: SvgPicture.file( - File( - ref.watch(coinIconProvider(coin)), - ), + File(ref.watch(coinIconProvider(coin))), width: 20, height: 20, ), ), ), - const SizedBox( - width: 12, - ), + const SizedBox(width: 12), if (isDesktop) Expanded( child: Row( @@ -117,11 +110,7 @@ class _ManagedFavoriteCardState extends ConsumerState { ), Expanded( child: Text( - ref - .watch( - pAmountFormatter(coin), - ) - .format(total), + ref.watch(pAmountFormatter(coin)).format(total), style: STextStyles.itemSubtitle(context), ), ), @@ -129,16 +118,16 @@ class _ManagedFavoriteCardState extends ConsumerState { isFavourite ? "Remove from favorites" : "Add to favorites", - style: - STextStyles.desktopTextExtraSmall(context).copyWith( - color: isFavourite - ? Theme.of(context) - .extension()! - .accentColorRed - : Theme.of(context) - .extension()! - .buttonTextBorderless, - ), + style: STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: isFavourite + ? Theme.of(context) + .extension()! + .accentColorRed + : Theme.of(context) + .extension()! + .buttonTextBorderless, + ), ), ], ), @@ -153,15 +142,9 @@ class _ManagedFavoriteCardState extends ConsumerState { ref.watch(pWalletName(walletId)), style: STextStyles.titleBold12(context), ), - const SizedBox( - height: 2, - ), + const SizedBox(height: 2), Text( - ref - .watch( - pAmountFormatter(coin), - ) - .format(total), + ref.watch(pAmountFormatter(coin)).format(total), style: STextStyles.itemSubtitle(context), ), ], From 1b15e3ccd5bc24ba9b0b31453dad9ca0d1810fdb Mon Sep 17 00:00:00 2001 From: sneurlax Date: Mon, 24 Aug 2026 18:53:34 -0500 Subject: [PATCH 2/6] feat: add a hide balances preference Adds the persisted `hideBalances` preference with mobile and desktop appearance toggles, plus a `HideableBalance` wrapper that replaces a balance with a labelled reveal control until it is tapped. --- .../appearance_settings_view.dart | 59 ++++++++++ .../appearance_settings.dart | 39 +++++++ lib/providers/global/prefs_provider.dart | 16 +-- lib/utilities/prefs.dart | 27 +++++ lib/widgets/hideable_balance.dart | 91 +++++++++++++++ test/widget_tests/hideable_balance_test.dart | 104 ++++++++++++++++++ 6 files changed, 328 insertions(+), 8 deletions(-) create mode 100644 lib/widgets/hideable_balance.dart create mode 100644 test/widget_tests/hideable_balance_test.dart diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/appearance_settings_view.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/appearance_settings_view.dart index ef37d5db6c..d5dcc842fa 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/appearance_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/appearance_settings_view.dart @@ -114,6 +114,65 @@ class AppearanceSettingsView extends ConsumerWidget { ), ), const SizedBox(height: 10), + RoundedWhiteContainer( + child: Consumer( + builder: (_, ref, __) { + return RawMaterialButton( + splashColor: Theme.of(context) + .extension()! + .highlight, + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + onPressed: null, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + ), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Hide balances", + style: STextStyles.titleBold12( + context, + ), + textAlign: TextAlign.left, + ), + SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: ref.watch( + prefsChangeNotifierProvider + .select( + (value) => + value.hideBalances, + ), + ), + onValueChanged: (newValue) { + ref + .read( + prefsChangeNotifierProvider, + ) + .hideBalances = + newValue; + }, + ), + ), + ], + ), + ), + ); + }, + ), + ), + const SizedBox(height: 10), RoundedWhiteContainer( child: Column( children: [ diff --git a/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/appearance_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/appearance_settings.dart index 711628e37c..36db9a7350 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/appearance_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/appearance_settings/appearance_settings.dart @@ -138,6 +138,45 @@ class _AppearanceOptionSettings padding: EdgeInsets.all(10.0), child: Divider(thickness: 0.5), ), + Padding( + padding: const EdgeInsets.all(10.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Hide balances", + style: STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + textAlign: TextAlign.left, + ), + SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.hideBalances, + ), + ), + onValueChanged: (newValue) { + ref + .read(prefsChangeNotifierProvider) + .hideBalances = + newValue; + }, + ), + ), + ], + ), + ), + const Padding( + padding: EdgeInsets.all(10.0), + child: Divider(thickness: 0.5), + ), Padding( padding: const EdgeInsets.all(10.0), child: Row( diff --git a/lib/providers/global/prefs_provider.dart b/lib/providers/global/prefs_provider.dart index fdcac56dab..2b79b31cb2 100644 --- a/lib/providers/global/prefs_provider.dart +++ b/lib/providers/global/prefs_provider.dart @@ -8,16 +8,16 @@ * */ -import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../utilities/prefs.dart'; -int _count = 0; -final prefsChangeNotifierProvider = ChangeNotifierProvider((_) { - if (kDebugMode) { - _count++; - } +final prefsChangeNotifierProvider = ChangeNotifierProvider( + (_) => Prefs.instance, +); - return Prefs.instance; -}); +final hideBalancesProvider = Provider( + (ref) => ref.watch( + prefsChangeNotifierProvider.select((value) => value.hideBalances), + ), +); diff --git a/lib/utilities/prefs.dart b/lib/utilities/prefs.dart index 36677bd6d6..15e4ca0eb7 100644 --- a/lib/utilities/prefs.dart +++ b/lib/utilities/prefs.dart @@ -83,6 +83,7 @@ class Prefs extends ChangeNotifier { _autoLockInfo = await _getAutoLockInfo(); _privacyScreen = await _getPrivacyScreen(); _disableScreenShots = await _getDisableScreenShots(); + _hideBalances = await _getHideBalances(); _initialized = true; } @@ -1391,4 +1392,30 @@ class Prefs extends ChangeNotifier { ) as bool? ?? false; } + + // hide balances + + bool _hideBalances = false; + + bool get hideBalances => _hideBalances; + + set hideBalances(bool hideBalances) { + if (_hideBalances != hideBalances) { + DB.instance.put( + boxName: DB.boxNamePrefs, + key: "hideBalances", + value: hideBalances, + ); + _hideBalances = hideBalances; + notifyListeners(); + } + } + + Future _getHideBalances() async { + return await DB.instance.get( + boxName: DB.boxNamePrefs, + key: "hideBalances", + ) as bool? ?? + false; + } } diff --git a/lib/widgets/hideable_balance.dart b/lib/widgets/hideable_balance.dart new file mode 100644 index 0000000000..a6b220cebb --- /dev/null +++ b/lib/widgets/hideable_balance.dart @@ -0,0 +1,91 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; + +import '../providers/global/prefs_provider.dart'; +import '../utilities/assets.dart'; + +/// Masks a balance and temporarily reveals it without changing the preference. +class HideableBalance extends ConsumerStatefulWidget { + const HideableBalance({ + super.key, + required this.child, + this.iconColor, + this.iconSize = 20, + }); + + final Widget child; + final Color? iconColor; + final double iconSize; + + static const Duration revealDuration = Duration(seconds: 3); + + @override + ConsumerState createState() => _HideableBalanceState(); +} + +class _HideableBalanceState extends ConsumerState { + Timer? _revealTimer; + bool _revealed = false; + + void _reveal() { + _revealTimer?.cancel(); + setState(() { + _revealed = true; + }); + _revealTimer = Timer(HideableBalance.revealDuration, () { + if (mounted) { + setState(() { + _revealed = false; + }); + } + }); + } + + @override + void dispose() { + _revealTimer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final hideBalances = ref.watch(hideBalancesProvider); + + if (!hideBalances || _revealed) { + return widget.child; + } + + // IconButton publishes the button/enabled/focus flags, the focus action + // and the tooltip as the accessible name; an enclosing Semantics node + // would mask all of it. + return IconButton( + tooltip: "Show balance temporarily", + onPressed: _reveal, + padding: EdgeInsets.zero, + constraints: const BoxConstraints(minWidth: 24, minHeight: 24), + visualDensity: VisualDensity.compact, + icon: SvgPicture.asset( + Assets.svg.eyeSlash, + semanticsLabel: "Show balance temporarily", + colorFilter: widget.iconColor == null + ? null + : ColorFilter.mode(widget.iconColor!, BlendMode.srcIn), + width: widget.iconSize, + height: widget.iconSize, + ), + ); + } +} diff --git a/test/widget_tests/hideable_balance_test.dart b/test/widget_tests/hideable_balance_test.dart new file mode 100644 index 0000000000..8ce3f6e807 --- /dev/null +++ b/test/widget_tests/hideable_balance_test.dart @@ -0,0 +1,104 @@ +import 'dart:ui' show SemanticsAction, Tristate; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/widgets/hideable_balance.dart'; + +void main() { + Widget testApp({required bool hidden, required Widget child}) { + return ProviderScope( + overrides: [hideBalancesProvider.overrideWithValue(hidden)], + child: MaterialApp(home: Scaffold(body: child)), + ); + } + + testWidgets("shows the balance when masking is disabled", (tester) async { + await tester.pumpWidget( + testApp( + hidden: false, + child: const HideableBalance(child: Text("12.34 BTC")), + ), + ); + + expect(find.text("12.34 BTC"), findsOneWidget); + expect(find.byType(IconButton), findsNothing); + }); + + testWidgets("exposes a labeled keyboard-accessible reveal control", ( + tester, + ) async { + final semantics = tester.ensureSemantics(); + + await tester.pumpWidget( + testApp( + hidden: true, + child: const HideableBalance(child: Text("12.34 BTC")), + ), + ); + + final control = find.byType(IconButton); + expect(find.text("12.34 BTC"), findsNothing); + expect( + tester.widget(control).tooltip, + "Show balance temporarily", + ); + expect(tester.getSize(control).shortestSide, greaterThanOrEqualTo(24)); + + final semanticsNode = tester.getSemantics(control); + final semanticsData = semanticsNode.getSemanticsData(); + expect(semanticsData.tooltip, contains("Show balance temporarily")); + expect(semanticsNode.flagsCollection.isButton, isTrue); + expect(semanticsNode.flagsCollection.isEnabled, Tristate.isTrue); + expect(semanticsData.hasAction(SemanticsAction.tap), isTrue); + expect(semanticsData.hasAction(SemanticsAction.focus), isTrue); + + await tester.sendKeyEvent(LogicalKeyboardKey.tab); + await tester.pump(); + expect( + tester.getSemantics(control).flagsCollection.isFocused, + Tristate.isTrue, + reason: "screen readers need the focus flag on desktop", + ); + + await tester.sendKeyEvent(LogicalKeyboardKey.tab); + await tester.pump(); + await tester.sendKeyEvent(LogicalKeyboardKey.enter); + await tester.pump(); + + expect(find.text("12.34 BTC"), findsOneWidget); + expect(control, findsNothing); + + await tester.pump(HideableBalance.revealDuration); + expect(find.text("12.34 BTC"), findsNothing); + expect(find.byType(IconButton), findsOneWidget); + semantics.dispose(); + }); + + testWidgets("reveals only the selected balance", (tester) async { + await tester.pumpWidget( + testApp( + hidden: true, + child: const Column( + children: [ + HideableBalance(child: Text("1 BTC")), + HideableBalance(child: Text(r"$2")), + ], + ), + ), + ); + + await tester.tap(find.byType(IconButton).first); + await tester.pump(); + + expect(find.text("1 BTC"), findsOneWidget); + expect(find.text(r"$2"), findsNothing); + expect(find.byType(IconButton), findsOneWidget); + + await tester.pumpWidget(const SizedBox()); + await tester.pump(HideableBalance.revealDuration); + expect(tester.takeException(), isNull); + }); +} From fd90bdb958a70b86cdd127e1aaeaeae4e4dd09e1 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Mon, 24 Aug 2026 18:53:40 -0500 Subject: [PATCH 3/6] feat: mask balances on every view that prints one Wraps the wallet and token summaries, favourite cards, wallet list rows, the balance toggle sheet, the token list and manage favourites, so the preference holds on every screen reachable from a masked one. --- .../sub_widgets/my_token_select_item.dart | 66 +++++---- .../token_view/sub_widgets/token_summary.dart | 49 ++++--- .../sub_widgets/token_summary_sol.dart | 42 ++++-- .../wallet_balance_toggle_sheet.dart | 19 ++- .../sub_widgets/wallet_summary_info.dart | 39 ++++-- .../sub_widgets/favorite_card.dart | 46 +++--- .../sub_widgets/desktop_wallet_summary.dart | 36 +++-- .../firo_desktop_wallet_summary.dart | 30 ++-- .../mweb_desktop_wallet_summary.dart | 30 ++-- lib/widgets/managed_favorite.dart | 27 +++- .../sub_widgets/wallet_info_row_balance.dart | 30 ++-- .../hide_balances_coverage_test.dart | 132 ++++++++++++++++++ 12 files changed, 405 insertions(+), 141 deletions(-) create mode 100644 test/widget_tests/hide_balances_coverage_test.dart diff --git a/lib/pages/token_view/sub_widgets/my_token_select_item.dart b/lib/pages/token_view/sub_widgets/my_token_select_item.dart index 8fe5fcf27a..f6917aa16c 100644 --- a/lib/pages/token_view/sub_widgets/my_token_select_item.dart +++ b/lib/pages/token_view/sub_widgets/my_token_select_item.dart @@ -32,6 +32,7 @@ import '../../../wallets/wallet/impl/sub_wallets/eth_token_wallet.dart'; import '../../../wallets/wallet/wallet.dart'; import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/dialogs/basic_dialog.dart'; +import '../../../widgets/hideable_balance.dart'; import '../../../widgets/icon_widgets/eth_token_icon.dart'; import '../../../widgets/rounded_white_container.dart'; import '../token_view.dart'; @@ -185,32 +186,47 @@ class _MyTokenSelectItemState extends ConsumerState { : STextStyles.titleBold12(context), ), const Spacer(), - Text( - ref - .watch( - pAmountFormatter( - Ethereum(CryptoCurrencyNetwork.main), + HideableBalance( + key: ValueKey( + "hideBalance_${widget.walletId}_" + "${widget.token.address}", + ), + iconColor: isDesktop + ? Theme.of(context) + .extension()! + .textDark + : Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch( + pAmountFormatter( + Ethereum(CryptoCurrencyNetwork.main), + ), + ) + .format( + ref + .watch( + pTokenBalance(( + walletId: widget.walletId, + contractAddress: + widget.token.address, + )), + ) + .total, + tokenContract: widget.token, ), - ) - .format( - ref - .watch( - pTokenBalance(( - walletId: widget.walletId, - contractAddress: widget.token.address, - )), - ) - .total, - tokenContract: widget.token, - ), - style: isDesktop - ? STextStyles.desktopTextExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ) - : STextStyles.itemSubtitle(context), + style: isDesktop + ? STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ) + : STextStyles.itemSubtitle(context), + ), ), ], ), diff --git a/lib/pages/token_view/sub_widgets/token_summary.dart b/lib/pages/token_view/sub_widgets/token_summary.dart index 4119b38501..ec0dd346df 100644 --- a/lib/pages/token_view/sub_widgets/token_summary.dart +++ b/lib/pages/token_view/sub_widgets/token_summary.dart @@ -36,6 +36,7 @@ import '../../../wallets/isar/providers/eth/token_balance_provider.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../widgets/coin_ticker_tag.dart'; import '../../../widgets/conditional_parent.dart'; +import '../../../widgets/hideable_balance.dart'; import '../../../widgets/rounded_container.dart'; import '../../buy_view/buy_in_wallet_view.dart'; import '../../exchange_view/wallet_initiated_exchange_view.dart'; @@ -104,18 +105,24 @@ class TokenSummary extends ConsumerWidget { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - ref - .watch( - pAmountFormatter( - Ethereum(CryptoCurrencyNetwork.main), - ), - ) - .format(balance.total, tokenContract: token), - style: STextStyles.pageTitleH1(context).copyWith( - color: Theme.of(context) - .extension()! - .tokenSummaryTextPrimary, + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, + iconSize: 24, + child: Text( + ref + .watch( + pAmountFormatter( + Ethereum(CryptoCurrencyNetwork.main), + ), + ) + .format(balance.total, tokenContract: token), + style: STextStyles.pageTitleH1(context).copyWith( + color: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, + ), ), ), const SizedBox(width: 10), @@ -128,12 +135,18 @@ class TokenSummary extends ConsumerWidget { ), if (price != null) const SizedBox(height: 6), if (price != null) - Text( - "${(balance.total.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", - style: STextStyles.subtitle500(context).copyWith( - color: Theme.of(context) - .extension()! - .tokenSummaryTextPrimary, + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, + iconSize: 16, + child: Text( + "${(balance.total.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", + style: STextStyles.subtitle500(context).copyWith( + color: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, + ), ), ), const SizedBox(height: 20), diff --git a/lib/pages/token_view/sub_widgets/token_summary_sol.dart b/lib/pages/token_view/sub_widgets/token_summary_sol.dart index 76d1cc7771..2c9136bf1c 100644 --- a/lib/pages/token_view/sub_widgets/token_summary_sol.dart +++ b/lib/pages/token_view/sub_widgets/token_summary_sol.dart @@ -23,12 +23,12 @@ import '../../../utilities/amount/amount.dart'; import '../../../utilities/assets.dart'; import '../../../utilities/constants.dart'; import '../../../utilities/text_styles.dart'; -import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/isar/providers/solana/current_sol_token_wallet_provider.dart'; import '../../../wallets/isar/providers/solana/sol_token_balance_provider.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../widgets/coin_ticker_tag.dart'; import '../../../widgets/conditional_parent.dart'; +import '../../../widgets/hideable_balance.dart'; import '../../../widgets/rounded_container.dart'; import '../../receive_view/sol_token_receive_view.dart'; import '../../send_view/sol_token_send_view.dart'; @@ -120,14 +120,20 @@ class SolanaTokenSummary extends ConsumerWidget { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - balance.total.decimal.toStringAsFixed( - tokenWallet.tokenDecimals, - ), - style: STextStyles.pageTitleH1(context).copyWith( - color: Theme.of(context) - .extension()! - .tokenSummaryTextPrimary, + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, + iconSize: 24, + child: Text( + balance.total.decimal.toStringAsFixed( + tokenWallet.tokenDecimals, + ), + style: STextStyles.pageTitleH1(context).copyWith( + color: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, + ), ), ), const SizedBox(width: 10), @@ -136,12 +142,18 @@ class SolanaTokenSummary extends ConsumerWidget { ), if (price != null) const SizedBox(height: 6), if (price != null) - Text( - "${(balance.total.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", - style: STextStyles.subtitle500(context).copyWith( - color: Theme.of(context) - .extension()! - .tokenSummaryTextPrimary, + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, + iconSize: 16, + child: Text( + "${(balance.total.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", + style: STextStyles.subtitle500(context).copyWith( + color: Theme.of(context) + .extension()! + .tokenSummaryTextPrimary, + ), ), ), const SizedBox(height: 20), diff --git a/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart b/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart index 8e4e22a631..f3d8f0a6e1 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart @@ -22,6 +22,7 @@ import '../../../utilities/enums/wallet_balance_toggle_state.dart'; import '../../../utilities/text_styles.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../../widgets/hideable_balance.dart'; enum _BalanceType { available, full, privateAvailable, privateFull } @@ -255,12 +256,18 @@ class BalanceSelector extends ConsumerWidget { children: [ Text(title, style: STextStyles.titleBold12(context)), const SizedBox(height: 2), - Text( - ref.watch(pAmountFormatter(coin)).format(balance), - style: STextStyles.itemSubtitle12(context).copyWith( - color: Theme.of(context) - .extension()! - .textSubtitle1, + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .textSubtitle1, + iconSize: 16, + child: Text( + ref.watch(pAmountFormatter(coin)).format(balance), + style: STextStyles.itemSubtitle12(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ), ), ), ], diff --git a/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart b/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart index eccb143847..704f8d8367 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart @@ -33,6 +33,7 @@ import '../../../wallets/crypto_currency/coins/firo.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../wallets/wallet/impl/banano_wallet.dart'; import '../../../widgets/conditional_parent.dart'; +import '../../../widgets/hideable_balance.dart'; import 'wallet_balance_toggle_sheet.dart'; import 'wallet_refresh_button.dart'; @@ -203,23 +204,35 @@ class WalletSummaryInfo extends ConsumerWidget { const Spacer(), FittedBox( fit: BoxFit.scaleDown, - child: SelectableText( - ref.watch(pAmountFormatter(coin)).format(balanceToShow), - style: STextStyles.pageTitleH1(context).copyWith( - fontSize: 24, - color: Theme.of(context) - .extension()! - .textFavoriteCard, + child: HideableBalance( + iconColor: Theme.of(context) + .extension()! + .textFavoriteCard, + iconSize: 24, + child: SelectableText( + ref.watch(pAmountFormatter(coin)).format(balanceToShow), + style: STextStyles.pageTitleH1(context).copyWith( + fontSize: 24, + color: Theme.of(context) + .extension()! + .textFavoriteCard, + ), ), ), ), if (price != null) - Text( - "${(price.value * balanceToShow.decimal).toAmount(fractionDigits: 2).fiatString(locale: locale)} $baseCurrency", - style: STextStyles.subtitle500(context).copyWith( - color: Theme.of(context) - .extension()! - .textFavoriteCard, + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .textFavoriteCard, + iconSize: 16, + child: Text( + "${(price.value * balanceToShow.decimal).toAmount(fractionDigits: 2).fiatString(locale: locale)} $baseCurrency", + style: STextStyles.subtitle500(context).copyWith( + color: Theme.of(context) + .extension()! + .textFavoriteCard, + ), ), ), ], diff --git a/lib/pages/wallets_view/sub_widgets/favorite_card.dart b/lib/pages/wallets_view/sub_widgets/favorite_card.dart index 3a0df66ebb..da5e017277 100644 --- a/lib/pages/wallets_view/sub_widgets/favorite_card.dart +++ b/lib/pages/wallets_view/sub_widgets/favorite_card.dart @@ -31,6 +31,7 @@ import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../wallets/wallet/intermediate/external_wallet.dart'; import '../../../widgets/coin_card.dart'; import '../../../widgets/conditional_parent.dart'; +import '../../../widgets/hideable_balance.dart'; import '../../wallet_view/wallet_view.dart'; class FavoriteCard extends ConsumerStatefulWidget { @@ -239,28 +240,41 @@ class _FavoriteCardState extends ConsumerState { children: [ FittedBox( fit: BoxFit.scaleDown, - child: Text( - ref.watch(pAmountFormatter(coin)).format(total), - style: STextStyles.titleBold12(context).copyWith( - fontSize: 16, - color: Theme.of(context) - .extension()! - .textFavoriteCard, + child: HideableBalance( + iconColor: Theme.of(context) + .extension()! + .textFavoriteCard, + iconSize: 16, + child: Text( + ref.watch(pAmountFormatter(coin)).format(total), + style: STextStyles.titleBold12(context) + .copyWith( + fontSize: 16, + color: Theme.of(context) + .extension()! + .textFavoriteCard, + ), ), ), ), if (externalCalls && price != null) const SizedBox(height: 4), if (externalCalls && price != null) - Text( - "${fiatTotal.fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", - style: STextStyles.itemSubtitle12(context) - .copyWith( - fontSize: 10, - color: Theme.of(context) - .extension()! - .textFavoriteCard, - ), + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .textFavoriteCard, + iconSize: 10, + child: Text( + "${fiatTotal.fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", + style: STextStyles.itemSubtitle12(context) + .copyWith( + fontSize: 10, + color: Theme.of(context) + .extension()! + .textFavoriteCard, + ), + ), ), ], ); diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart index 46ec1bcfe3..ebb8960965 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart @@ -33,6 +33,7 @@ import '../../../../wallets/isar/providers/eth/token_balance_provider.dart'; import '../../../../wallets/isar/providers/solana/current_sol_token_wallet_provider.dart'; import '../../../../wallets/isar/providers/solana/sol_token_balance_provider.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../../../widgets/hideable_balance.dart'; import 'desktop_balance_toggle_button.dart'; class DesktopWalletSummary extends ConsumerStatefulWidget { @@ -171,20 +172,33 @@ class _WDesktopWalletSummaryState extends ConsumerState { children: [ FittedBox( fit: BoxFit.scaleDown, - child: SelectableText( - ref - .watch(pAmountFormatter(coin)) - .format(balanceToShow, tokenContract: tokenContract), - style: STextStyles.desktopH3(context), + child: HideableBalance( + iconColor: Theme.of(context) + .extension()! + .textDark, + iconSize: 24, + child: SelectableText( + ref + .watch(pAmountFormatter(coin)) + .format(balanceToShow, tokenContract: tokenContract), + style: STextStyles.desktopH3(context), + ), ), ), if (externalCalls && price != null) - SelectableText( - "${Amount.fromDecimal(price.value * balanceToShow.decimal, fractionDigits: 2).fiatString(locale: locale)} $baseCurrency", - style: STextStyles.desktopTextExtraSmall(context).copyWith( - color: Theme.of(context) - .extension()! - .textSubtitle1, + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .textSubtitle1, + iconSize: 16, + child: SelectableText( + "${Amount.fromDecimal(price.value * balanceToShow.decimal, fractionDigits: 2).fiatString(locale: locale)} $baseCurrency", + style: STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ), ), ), // if (coin is Firo) diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/firo_desktop_wallet_summary.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/firo_desktop_wallet_summary.dart index e193ab380c..5728276544 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/firo_desktop_wallet_summary.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/firo_desktop_wallet_summary.dart @@ -27,6 +27,7 @@ import '../../../../utilities/extensions/extensions.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../wallets/crypto_currency/coins/firo.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../../../widgets/hideable_balance.dart'; import 'desktop_balance_toggle_button.dart'; class FiroDesktopWalletSummary extends ConsumerStatefulWidget { @@ -194,10 +195,14 @@ class _Balance extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return SelectableText( - ref.watch(pAmountFormatter(coin)).format(amount, tokenContract: null), - style: STextStyles.desktopH3(context), - textAlign: TextAlign.end, + return HideableBalance( + iconColor: Theme.of(context).extension()!.textDark, + iconSize: 24, + child: SelectableText( + ref.watch(pAmountFormatter(coin)).format(amount, tokenContract: null), + style: STextStyles.desktopH3(context), + textAlign: TextAlign.end, + ), ); } } @@ -218,14 +223,17 @@ class _Price extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return Padding( padding: const EdgeInsets.only(left: 16), - child: SelectableText( - "${Amount.fromDecimal(price * amount.decimal, fractionDigits: 2).fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} " - "${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", - style: STextStyles.desktopTextExtraSmall(context).copyWith( - color: Theme.of(context).extension()!.textSubtitle1, + child: HideableBalance( + iconColor: Theme.of(context).extension()!.textSubtitle1, + iconSize: 16, + child: SelectableText( + "${Amount.fromDecimal(price * amount.decimal, fractionDigits: 2).fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} " + "${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", + style: STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of(context).extension()!.textSubtitle1, + ), + textAlign: TextAlign.end, ), - - textAlign: TextAlign.end, ), ); } diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/mweb_desktop_wallet_summary.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/mweb_desktop_wallet_summary.dart index 6b025f0dd7..765e6734b9 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/mweb_desktop_wallet_summary.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/mweb_desktop_wallet_summary.dart @@ -23,6 +23,7 @@ import '../../../../utilities/enums/wallet_balance_toggle_state.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../../../widgets/hideable_balance.dart'; import 'desktop_balance_toggle_button.dart'; class MwebDesktopWalletSummary extends ConsumerStatefulWidget { @@ -169,10 +170,14 @@ class _Balance extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return SelectableText( - ref.watch(pAmountFormatter(coin)).format(amount, tokenContract: null), - style: STextStyles.desktopH3(context), - textAlign: TextAlign.end, + return HideableBalance( + iconColor: Theme.of(context).extension()!.textDark, + iconSize: 24, + child: SelectableText( + ref.watch(pAmountFormatter(coin)).format(amount, tokenContract: null), + style: STextStyles.desktopH3(context), + textAlign: TextAlign.end, + ), ); } } @@ -193,14 +198,17 @@ class _Price extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return Padding( padding: const EdgeInsets.only(left: 16), - child: SelectableText( - "${Amount.fromDecimal(price * amount.decimal, fractionDigits: 2).fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} " - "${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", - style: STextStyles.desktopTextExtraSmall(context).copyWith( - color: Theme.of(context).extension()!.textSubtitle1, + child: HideableBalance( + iconColor: Theme.of(context).extension()!.textSubtitle1, + iconSize: 16, + child: SelectableText( + "${Amount.fromDecimal(price * amount.decimal, fractionDigits: 2).fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} " + "${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", + style: STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of(context).extension()!.textSubtitle1, + ), + textAlign: TextAlign.end, ), - - textAlign: TextAlign.end, ), ); } diff --git a/lib/widgets/managed_favorite.dart b/lib/widgets/managed_favorite.dart index f1e66683c3..39b55c6517 100644 --- a/lib/widgets/managed_favorite.dart +++ b/lib/widgets/managed_favorite.dart @@ -26,6 +26,7 @@ import '../utilities/util.dart'; import '../wallets/crypto_currency/coins/firo.dart'; import '../wallets/isar/providers/wallet_info_provider.dart'; import 'custom_buttons/favorite_toggle.dart'; +import 'hideable_balance.dart'; import 'rounded_white_container.dart'; class ManagedFavorite extends ConsumerStatefulWidget { @@ -109,9 +110,16 @@ class _ManagedFavoriteCardState extends ConsumerState { ), ), Expanded( - child: Text( - ref.watch(pAmountFormatter(coin)).format(total), - style: STextStyles.itemSubtitle(context), + child: HideableBalance( + key: ValueKey("hideBalance_$walletId"), + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref.watch(pAmountFormatter(coin)).format(total), + style: STextStyles.itemSubtitle(context), + ), ), ), Text( @@ -143,9 +151,16 @@ class _ManagedFavoriteCardState extends ConsumerState { style: STextStyles.titleBold12(context), ), const SizedBox(height: 2), - Text( - ref.watch(pAmountFormatter(coin)).format(total), - style: STextStyles.itemSubtitle(context), + HideableBalance( + key: ValueKey("hideBalance_$walletId"), + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref.watch(pAmountFormatter(coin)).format(total), + style: STextStyles.itemSubtitle(context), + ), ), ], ), diff --git a/lib/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart b/lib/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart index a1ec77214b..3bf2a02dd6 100644 --- a/lib/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart +++ b/lib/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart @@ -23,6 +23,7 @@ import '../../../wallets/crypto_currency/coins/solana.dart'; import '../../../wallets/isar/providers/eth/token_balance_provider.dart'; import '../../../wallets/isar/providers/solana/sol_token_balance_provider.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../hideable_balance.dart'; class WalletInfoRowBalance extends ConsumerWidget { const WalletInfoRowBalance({ @@ -88,15 +89,26 @@ class WalletInfoRowBalance extends ConsumerWidget { } } - return Text( - ref - .watch(pAmountFormatter(info.coin)) - .format(totalBalance, tokenContract: contract), - style: Util.isDesktop - ? STextStyles.desktopTextExtraSmall(context).copyWith( - color: Theme.of(context).extension()!.textSubtitle1, - ) - : STextStyles.itemSubtitle(context), + return HideableBalance( + // These rows live in sorted/filtered lists; keep reveal state pinned to + // the balance being shown rather than to the row's position. + key: ValueKey("hideBalance_${walletId}_${contractAddress}_$balanceType"), + iconColor: Util.isDesktop + ? Theme.of(context).extension()!.textSubtitle1 + : Theme.of(context).extension()!.infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch(pAmountFormatter(info.coin)) + .format(totalBalance, tokenContract: contract), + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ) + : STextStyles.itemSubtitle(context), + ), ); } } diff --git a/test/widget_tests/hide_balances_coverage_test.dart b/test/widget_tests/hide_balances_coverage_test.dart new file mode 100644 index 0000000000..3652fb9736 --- /dev/null +++ b/test/widget_tests/hide_balances_coverage_test.dart @@ -0,0 +1,132 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/models/balance.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'; +import 'package:stackwallet/models/isar/stack_theme.dart'; +import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart'; +import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/themes/coin_icon_provider.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/themes/theme_providers.dart'; +import 'package:stackwallet/utilities/amount/amount.dart'; +import 'package:stackwallet/utilities/amount/amount_formatter.dart'; +import 'package:stackwallet/utilities/amount/amount_unit.dart'; +import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; +import 'package:stackwallet/wallets/isar/models/wallet_info.dart'; +import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart'; +import 'package:stackwallet/widgets/hideable_balance.dart'; +import 'package:stackwallet/widgets/managed_favorite.dart'; +import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart'; + +import '../sample_data/theme_json.dart'; + +void main() { + const walletId = "some-wallet-id"; + final coin = Bitcoin(CryptoCurrencyNetwork.main); + + final total = Amount( + rawValue: BigInt.from(123456789), + fractionDigits: coin.fractionDigits, + ); + + final formatter = AmountFormatter( + unit: AmountUnit.normal, + locale: "en_US", + coin: coin, + maxDecimals: 8, + ); + + final formatted = formatter.format(total); + + final balance = Balance( + total: total, + spendable: total, + blockedTotal: Amount.zeroWith(fractionDigits: coin.fractionDigits), + pendingSpendable: total, + ); + + final theme = StackTheme.fromJson(json: lightThemeJsonMap); + + Widget app({required bool hidden, required Widget child}) { + return ProviderScope( + overrides: [ + hideBalancesProvider.overrideWithValue(hidden), + pAmountFormatter(coin).overrideWithValue(formatter), + themeProvider.overrideWithProvider(StateProvider((_) => theme)), + coinIconProvider.overrideWithProvider( + (_) => Provider( + (_) => + "${Directory.current.path}/test/sample_data/light/assets/dummy.svg", + ), + ), + pWalletInfo(walletId).overrideWithValue( + WalletInfo( + walletId: walletId, + name: "some wallet", + mainAddressType: AddressType.p2wpkh, + coinName: coin.identifier, + cachedBalanceString: balance.toJsonIgnoreCoin(), + ), + ), + pWalletName(walletId).overrideWithValue("some wallet"), + pWalletCoin(walletId).overrideWithValue(coin), + pWalletBalance(walletId).overrideWithValue(balance), + pWalletIsFavourite(walletId).overrideWithValue(false), + ], + child: MaterialApp( + theme: ThemeData(extensions: [StackColors.fromStackColorTheme(theme)]), + home: Scaffold(body: child), + ), + ); + } + + Future expectMasking( + WidgetTester tester, + Widget Function() build, + Finder balance, + ) async { + await tester.pumpWidget(app(hidden: true, child: build())); + await tester.pump(); + expect(balance, findsNothing); + expect(find.byType(HideableBalance), findsWidgets); + + await tester.pumpWidget(app(hidden: false, child: build())); + await tester.pump(); + expect(balance, findsWidgets); + } + + testWidgets("wallet list rows mask their balance", (tester) async { + await expectMasking( + tester, + () => const WalletInfoRowBalance(walletId: walletId), + find.text(formatted), + ); + }); + + testWidgets("the balance toggle sheet masks its balances", (tester) async { + await expectMasking( + tester, + () => BalanceSelector( + title: "Available balance", + coin: coin, + balance: total, + onPressed: () {}, + onChanged: (_) {}, + value: 0, + groupValue: 0, + ), + find.text(formatted), + ); + }); + + testWidgets("manage favorites masks wallet balances", (tester) async { + await expectMasking( + tester, + () => const ManagedFavorite(walletId: walletId), + find.text(formatted), + ); + }); +} From 75f65b0a8559f319d8b060a73910a3d6fb3410b4 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 6 Oct 2026 21:46:20 -0500 Subject: [PATCH 4/6] style(balances): dart format send and wallet list views before masking --- lib/pages/cakepay/cakepay_send_from_view.dart | 18 ++++---- lib/pages/exchange_view/send_from_view.dart | 36 +++++++-------- .../wallet_syncing_options_view.dart | 46 +++++++++---------- .../shopinbit/shopinbit_send_from_view.dart | 18 ++++---- .../sub_widgets/sol_token_select_item.dart | 38 ++++++++------- .../desktop_choose_address_from_stack.dart | 18 ++++---- .../paynym/desktop_paynym_send_dialog.dart | 19 ++++---- 7 files changed, 95 insertions(+), 98 deletions(-) diff --git a/lib/pages/cakepay/cakepay_send_from_view.dart b/lib/pages/cakepay/cakepay_send_from_view.dart index 4213625d36..f21d437ccb 100644 --- a/lib/pages/cakepay/cakepay_send_from_view.dart +++ b/lib/pages/cakepay/cakepay_send_from_view.dart @@ -80,9 +80,9 @@ class _CakePaySendFromViewState extends ConsumerState { builder: (child) { return Background( child: Scaffold( - backgroundColor: Theme.of( - context, - ).extension()!.background, + backgroundColor: Theme.of(context) + .extension()! + .background, appBar: AppBar( leading: AppBarBackButton( onPressed: () => Navigator.of(context).pop(), @@ -208,9 +208,9 @@ class _CakePaySendFromCardState extends ConsumerState<_CakePaySendFromCard> { child: Text( "Ok", style: STextStyles.button(context).copyWith( - color: Theme.of( - context, - ).extension()!.buttonTextSecondary, + color: Theme.of(context) + .extension()! + .buttonTextSecondary, ), ), onPressed: () => Navigator.of(context).pop(), @@ -327,9 +327,9 @@ class _CakePaySendFromCardState extends ConsumerState<_CakePaySendFromCard> { child: Text( "Ok", style: STextStyles.button(context).copyWith( - color: Theme.of( - context, - ).extension()!.buttonTextSecondary, + color: Theme.of(context) + .extension()! + .buttonTextSecondary, ), ), onPressed: () => Navigator.of(context).pop(), diff --git a/lib/pages/exchange_view/send_from_view.dart b/lib/pages/exchange_view/send_from_view.dart index 9fe4121ee0..d49dfdb4ea 100644 --- a/lib/pages/exchange_view/send_from_view.dart +++ b/lib/pages/exchange_view/send_from_view.dart @@ -106,9 +106,9 @@ class _SendFromViewState extends ConsumerState { builder: (child) { return Background( child: Scaffold( - backgroundColor: Theme.of( - context, - ).extension()!.background, + backgroundColor: Theme.of(context) + .extension()! + .background, appBar: AppBar( leading: AppBarBackButton( onPressed: () { @@ -381,9 +381,9 @@ class _SendFromCardState extends ConsumerState { child: Text( "Ok", style: STextStyles.button(context).copyWith( - color: Theme.of( - context, - ).extension()!.buttonTextSecondary, + color: Theme.of(context) + .extension()! + .buttonTextSecondary, ), ), onPressed: () { @@ -427,9 +427,9 @@ class _SendFromCardState extends ConsumerState { children: [ if (!trade.exchangeName.startsWith(TrocadorExchange.exchangeName)) MaterialButton( - splashColor: Theme.of( - context, - ).extension()!.highlight, + splashColor: Theme.of(context) + .extension()! + .highlight, key: Key("walletsSheetItemButtonFiroPrivateKey_$walletId"), padding: const EdgeInsets.all(0), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, @@ -481,9 +481,9 @@ class _SendFromCardState extends ConsumerState { Assets.svg.chevronRight, height: 14, width: 7, - color: Theme.of( - context, - ).extension()!.infoItemLabel, + color: Theme.of(context) + .extension()! + .infoItemLabel, ), ], ), @@ -491,9 +491,9 @@ class _SendFromCardState extends ConsumerState { ), ), MaterialButton( - splashColor: Theme.of( - context, - ).extension()!.highlight, + splashColor: Theme.of(context) + .extension()! + .highlight, key: Key("walletsSheetItemButtonFiroPublicKey_$walletId"), padding: const EdgeInsets.all(0), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, @@ -543,9 +543,9 @@ class _SendFromCardState extends ConsumerState { Assets.svg.chevronRight, height: 14, width: 7, - color: Theme.of( - context, - ).extension()!.infoItemLabel, + color: Theme.of(context) + .extension()! + .infoItemLabel, ), ], ), diff --git a/lib/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart b/lib/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart index c5916cf708..9e8d66a1de 100644 --- a/lib/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart +++ b/lib/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart @@ -44,8 +44,9 @@ class WalletSyncingOptionsView extends ConsumerWidget { builder: (child) { return Background( child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, + backgroundColor: Theme.of(context) + .extension()! + .background, appBar: AppBar( leading: AppBarBackButton( onPressed: () async { @@ -98,12 +99,11 @@ class WalletSyncingOptionsView extends ConsumerWidget { const SizedBox(height: 12), RoundedWhiteContainer( padding: const EdgeInsets.all(0), - borderColor: - !isDesktop - ? Colors.transparent - : Theme.of( - context, - ).extension()!.background, + borderColor: !isDesktop + ? Colors.transparent + : Theme.of(context) + .extension()! + .background, child: Column( children: [ ...walletInfos.map( @@ -177,24 +177,23 @@ class WalletSyncingOptionsView extends ConsumerWidget { child: DraggableSwitchButton( isOn: ref .watch( - prefsChangeNotifierProvider.select( - (value) => - value + prefsChangeNotifierProvider + .select( + (value) => value .walletIdsSyncOnStartup, - ), + ), ) .contains(info.walletId), onValueChanged: (value) { // final syncType = ref // .read(prefsChangeNotifierProvider) // .syncType; - final ids = - ref - .read( - prefsChangeNotifierProvider, - ) - .walletIdsSyncOnStartup - .toList(); + final ids = ref + .read( + prefsChangeNotifierProvider, + ) + .walletIdsSyncOnStartup + .toList(); if (value) { ids.add(info.walletId); } else { @@ -222,10 +221,11 @@ class WalletSyncingOptionsView extends ConsumerWidget { // } ref - .read( - prefsChangeNotifierProvider, - ) - .walletIdsSyncOnStartup = ids; + .read( + prefsChangeNotifierProvider, + ) + .walletIdsSyncOnStartup = + ids; }, ), ), diff --git a/lib/pages/shopinbit/shopinbit_send_from_view.dart b/lib/pages/shopinbit/shopinbit_send_from_view.dart index fb54b1f41b..f43e22f210 100644 --- a/lib/pages/shopinbit/shopinbit_send_from_view.dart +++ b/lib/pages/shopinbit/shopinbit_send_from_view.dart @@ -115,9 +115,9 @@ class _ShopInBitSendFromViewState extends ConsumerState { builder: (child) { return Background( child: Scaffold( - backgroundColor: Theme.of( - context, - ).extension()!.background, + backgroundColor: Theme.of(context) + .extension()! + .background, appBar: AppBar( leading: AppBarBackButton( onPressed: () { @@ -270,9 +270,9 @@ class _ShopInBitSendFromCardState extends ConsumerState { child: Text( "Ok", style: STextStyles.button(context).copyWith( - color: Theme.of( - context, - ).extension()!.buttonTextSecondary, + color: Theme.of(context) + .extension()! + .buttonTextSecondary, ), ), onPressed: () => Navigator.of(context).pop(), @@ -413,9 +413,9 @@ class _ShopInBitSendFromCardState extends ConsumerState { child: Text( "Ok", style: STextStyles.button(context).copyWith( - color: Theme.of( - context, - ).extension()!.buttonTextSecondary, + color: Theme.of(context) + .extension()! + .buttonTextSecondary, ), ), onPressed: () { diff --git a/lib/pages/token_view/sub_widgets/sol_token_select_item.dart b/lib/pages/token_view/sub_widgets/sol_token_select_item.dart index 498ef025c8..03d048947d 100644 --- a/lib/pages/token_view/sub_widgets/sol_token_select_item.dart +++ b/lib/pages/token_view/sub_widgets/sol_token_select_item.dart @@ -104,12 +104,12 @@ class _SolTokenSelectItemState extends ConsumerState { return; } - ref.read(solanaTokenServiceStateProvider.state).state = - Wallet.loadSolTokenWallet( - solWallet: solanaWallet, - contract: widget.token, - ) - as SolanaTokenWallet; + ref + .read(solanaTokenServiceStateProvider.state) + .state = Wallet.loadSolTokenWallet( + solWallet: solanaWallet, + contract: widget.token, + ) as SolanaTokenWallet; final success = await showLoading( whileFuture: _loadTokenWallet(context, ref), @@ -186,26 +186,24 @@ class _SolTokenSelectItemState extends ConsumerState { Text( widget.token.name, style: isDesktop - ? STextStyles.desktopTextExtraSmall( - context, - ).copyWith( - color: Theme.of( - context, - ).extension()!.textDark, - ) + ? STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ) : STextStyles.titleBold12(context), ), const Spacer(), Text( balanceString, style: isDesktop - ? STextStyles.desktopTextExtraSmall( - context, - ).copyWith( - color: Theme.of( - context, - ).extension()!.textDark, - ) + ? STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ) : STextStyles.itemSubtitle(context), ), ], diff --git a/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_address_from_stack.dart b/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_address_from_stack.dart index 8eaf949914..6dd9b2fe74 100644 --- a/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_address_from_stack.dart +++ b/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_address_from_stack.dart @@ -108,9 +108,9 @@ class _DesktopChooseFromStackState }); }, style: STextStyles.desktopTextExtraSmall(context).copyWith( - color: Theme.of( - context, - ).extension()!.textFieldActiveText, + color: Theme.of(context) + .extension()! + .textFieldActiveText, height: 1.8, ), decoration: @@ -167,9 +167,9 @@ class _DesktopChooseFromStackState return Column( children: [ RoundedWhiteContainer( - borderColor: Theme.of( - context, - ).extension()!.background, + borderColor: Theme.of(context) + .extension()! + .background, child: Center( child: Text( "No ${widget.coin.ticker.toUpperCase()} wallets", @@ -267,9 +267,9 @@ class _WalletRow extends ConsumerWidget { ref.watch(pWalletName(walletId)), style: STextStyles.desktopTextExtraExtraSmall(context) .copyWith( - color: Theme.of( - context, - ).extension()!.textDark, + color: Theme.of(context) + .extension()! + .textDark, ), ), ], diff --git a/lib/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart b/lib/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart index 1ff4405dfa..866e6a6ca7 100644 --- a/lib/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart @@ -92,8 +92,9 @@ class _DesktopPaynymSendDialogState Padding( padding: const EdgeInsets.symmetric(horizontal: 32), child: RoundedWhiteContainer( - borderColor: - Theme.of(context).extension()!.background, + borderColor: Theme.of(context) + .extension()! + .background, // Theme.of(context).extension()!.textSubtitle4, child: Row( children: [ @@ -116,10 +117,9 @@ class _DesktopPaynymSendDialogState Text( "Available balance", style: STextStyles.baseXS(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textSubtitle1, + color: Theme.of(context) + .extension()! + .textSubtitle1, ), ), ], @@ -146,10 +146,9 @@ class _DesktopPaynymSendDialogState Text( "${(ref.watch(pWalletBalance(widget.walletId)).spendable.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: locale)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", style: STextStyles.baseXS(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textSubtitle1, + color: Theme.of(context) + .extension()! + .textSubtitle1, ), textAlign: TextAlign.right, ), From 8a2d9b0765b6da50798e5fd72eb8cba3c371b985 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 6 Oct 2026 21:51:22 -0500 Subject: [PATCH 5/6] feat: mask balances on send screens Extends hide balances to the send views (coin, token, Solana token, FROST, desktop send), the send balance selection sheet, the paynym send dialog and the exchange, CakePay and ShopInBit send-from pickers. The Firo balance choice in exchange step 4 drops the amounts from its button labels while balances are hidden. --- lib/pages/cakepay/cakepay_send_from_view.dart | 23 +++-- .../exchange_step_views/step_4_view.dart | 11 ++- lib/pages/exchange_view/send_from_view.dart | 73 +++++++++------ .../send_view/frost_ms/frost_send_view.dart | 39 ++++---- lib/pages/send_view/send_view.dart | 89 ++++++++++++------- lib/pages/send_view/sol_token_send_view.dart | 59 ++++++------ .../dual_balance_selection_sheet.dart | 69 ++++++++------ lib/pages/send_view/token_send_view.dart | 73 ++++++++------- .../shopinbit/shopinbit_send_from_view.dart | 33 ++++--- .../desktop_choose_address_from_stack.dart | 15 ++-- .../paynym/desktop_paynym_send_dialog.dart | 53 ++++++----- .../wallet_view/sub_widgets/desktop_send.dart | 53 ++++++----- test/wallets/support/xelis_test_fakes.dart | 2 + .../hide_balances_coverage_test.dart | 12 +++ 14 files changed, 373 insertions(+), 231 deletions(-) diff --git a/lib/pages/cakepay/cakepay_send_from_view.dart b/lib/pages/cakepay/cakepay_send_from_view.dart index f21d437ccb..e7395f2d3b 100644 --- a/lib/pages/cakepay/cakepay_send_from_view.dart +++ b/lib/pages/cakepay/cakepay_send_from_view.dart @@ -28,6 +28,7 @@ import '../../widgets/conditional_parent.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/desktop/desktop_dialog.dart'; import '../../widgets/desktop/desktop_dialog_close_button.dart'; +import '../../widgets/hideable_balance.dart'; import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_dialog.dart'; import '../../pages_desktop_specific/desktop_home_view.dart'; @@ -389,13 +390,21 @@ class _CakePaySendFromCardState extends ConsumerState<_CakePaySendFromCard> { style: STextStyles.titleBold12(context), ), const SizedBox(height: 2), - Text( - ref - .watch(pAmountFormatter(coin)) - .format( - ref.watch(pWalletBalance(widget.walletId)).spendable, - ), - style: STextStyles.itemSubtitle(context), + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch(pAmountFormatter(coin)) + .format( + ref + .watch(pWalletBalance(widget.walletId)) + .spendable, + ), + style: STextStyles.itemSubtitle(context), + ), ), ], ), diff --git a/lib/pages/exchange_view/exchange_step_views/step_4_view.dart b/lib/pages/exchange_view/exchange_step_views/step_4_view.dart index dc75032f94..bceff00f3e 100644 --- a/lib/pages/exchange_view/exchange_step_views/step_4_view.dart +++ b/lib/pages/exchange_view/exchange_step_views/step_4_view.dart @@ -192,6 +192,7 @@ class _Step4ViewState extends ConsumerState { final coin = ref.read(pWalletCoin(walletId)); final balancePublic = ref.read(pWalletBalance(walletId)); final balancePrivate = ref.read(pWalletBalanceTertiary(walletId)); + final hideBalances = ref.read(hideBalancesProvider); return await showModalBottomSheet( context: context, @@ -217,14 +218,16 @@ class _Step4ViewState extends ConsumerState { ), const SizedBox(height: 32), SecondaryButton( - label: - "${ref.watch(pAmountFormatter(coin)).format(balancePrivate.spendable)} (private)", + label: hideBalances + ? "Private balance" + : "${ref.watch(pAmountFormatter(coin)).format(balancePrivate.spendable)} (private)", onPressed: () => Navigator.of(context).pop(false), ), const SizedBox(height: 16), SecondaryButton( - label: - "${ref.watch(pAmountFormatter(coin)).format(balancePublic.spendable)} (public)", + label: hideBalances + ? "Public balance" + : "${ref.watch(pAmountFormatter(coin)).format(balancePublic.spendable)} (public)", onPressed: () => Navigator.of(context).pop(true), ), const SizedBox(height: 32), diff --git a/lib/pages/exchange_view/send_from_view.dart b/lib/pages/exchange_view/send_from_view.dart index d49dfdb4ea..2c953911ac 100644 --- a/lib/pages/exchange_view/send_from_view.dart +++ b/lib/pages/exchange_view/send_from_view.dart @@ -43,6 +43,7 @@ import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/desktop/desktop_dialog.dart'; import '../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../widgets/expandable.dart'; +import '../../widgets/hideable_balance.dart'; import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_dialog.dart'; import '../home_view/home_view.dart'; @@ -463,17 +464,23 @@ class _SendFromCardState extends ConsumerState { "Use private balance", style: STextStyles.itemSubtitle(context), ), - Text( - ref - .watch(pAmountFormatter(coin)) - .format( - ref - .watch( - pWalletBalanceTertiary(walletId), - ) - .spendable, - ), - style: STextStyles.itemSubtitle(context), + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch(pAmountFormatter(coin)) + .format( + ref + .watch( + pWalletBalanceTertiary(walletId), + ) + .spendable, + ), + style: STextStyles.itemSubtitle(context), + ), ), ], ), @@ -527,15 +534,21 @@ class _SendFromCardState extends ConsumerState { "Use public balance", style: STextStyles.itemSubtitle(context), ), - Text( - ref - .watch(pAmountFormatter(coin)) - .format( - ref - .watch(pWalletBalance(walletId)) - .spendable, - ), - style: STextStyles.itemSubtitle(context), + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch(pAmountFormatter(coin)) + .format( + ref + .watch(pWalletBalance(walletId)) + .spendable, + ), + style: STextStyles.itemSubtitle(context), + ), ), ], ), @@ -605,13 +618,19 @@ class _SendFromCardState extends ConsumerState { ), if (!isFiro) const SizedBox(height: 2), if (!isFiro) - Text( - ref - .watch(pAmountFormatter(coin)) - .format( - ref.watch(pWalletBalance(walletId)).spendable, - ), - style: STextStyles.itemSubtitle(context), + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch(pAmountFormatter(coin)) + .format( + ref.watch(pWalletBalance(walletId)).spendable, + ), + style: STextStyles.itemSubtitle(context), + ), ), ], ), diff --git a/lib/pages/send_view/frost_ms/frost_send_view.dart b/lib/pages/send_view/frost_ms/frost_send_view.dart index ce67095ce5..07796e401c 100644 --- a/lib/pages/send_view/frost_ms/frost_send_view.dart +++ b/lib/pages/send_view/frost_ms/frost_send_view.dart @@ -43,6 +43,7 @@ import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/desktop/secondary_button.dart'; import '../../../widgets/fee_slider.dart'; import '../../../widgets/frost_scaffold.dart'; +import '../../../widgets/hideable_balance.dart'; import '../../../widgets/icon_widgets/x_icon.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_dialog.dart'; @@ -341,22 +342,28 @@ class _FrostSendViewState extends ConsumerState { onTap: () {}, child: Container( color: Colors.transparent, - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - ref - .watch(pAmountFormatter(coin)) - .format( - ref - .watch(pWalletBalance(walletId)) - .spendable, - ), - style: STextStyles.titleBold12(context) - .copyWith(fontSize: 10), - textAlign: TextAlign.right, - ), - ], + child: HideableBalance( + iconColor: Theme.of(context) + .extension()! + .textDark, + iconSize: 16, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + ref + .watch(pAmountFormatter(coin)) + .format( + ref + .watch(pWalletBalance(walletId)) + .spendable, + ), + style: STextStyles.titleBold12(context) + .copyWith(fontSize: 10), + textAlign: TextAlign.right, + ), + ], + ), ), ), ), diff --git a/lib/pages/send_view/send_view.dart b/lib/pages/send_view/send_view.dart index e6249006a7..2668c71101 100644 --- a/lib/pages/send_view/send_view.dart +++ b/lib/pages/send_view/send_view.dart @@ -72,6 +72,7 @@ import '../../widgets/dialogs/firo_exchange_address_dialog.dart'; import '../../widgets/epic_txs_method_toggle.dart'; import '../../widgets/eth_fee_form.dart'; import '../../widgets/fee_slider.dart'; +import '../../widgets/hideable_balance.dart'; import '../../widgets/icon_widgets/addressbook_icon.dart'; import '../../widgets/icon_widgets/clipboard_icon.dart'; import '../../widgets/icon_widgets/qrcode_icon.dart'; @@ -1655,31 +1656,45 @@ class _SendViewState extends ConsumerState { }, child: Container( color: Colors.transparent, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.end, - children: [ - Text( - ref - .watch( - pAmountFormatter(coin), - ) - .format(amount), - style: - STextStyles.titleBold12( - context, - ).copyWith(fontSize: 10), - textAlign: TextAlign.right, - ), - if (price != null) + child: HideableBalance( + iconColor: Theme.of(context) + .extension()! + .textDark, + iconSize: 16, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ Text( - "${(amount.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: locale)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", - style: STextStyles.subtitle( - context, - ).copyWith(fontSize: 8), + ref + .watch( + pAmountFormatter( + coin, + ), + ) + .format(amount), + style: + STextStyles.titleBold12( + context, + ).copyWith( + fontSize: 10, + ), textAlign: TextAlign.right, ), - ], + if (price != null) + Text( + "${(amount.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: locale)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", + style: + STextStyles.subtitle( + context, + ).copyWith( + fontSize: 8, + ), + textAlign: + TextAlign.right, + ), + ], + ), ), ), ); @@ -2175,18 +2190,26 @@ class _SendViewState extends ConsumerState { break; } - return Text( - ref - .watch( - pAmountFormatter( - coin, + return HideableBalance( + iconColor: Theme.of(context) + .extension< + StackColors + >()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch( + pAmountFormatter( + coin, + ), + ) + .format(amount), + style: + STextStyles.itemSubtitle( + context, ), - ) - .format(amount), - style: - STextStyles.itemSubtitle( - context, - ), + ), ); }, ), diff --git a/lib/pages/send_view/sol_token_send_view.dart b/lib/pages/send_view/sol_token_send_view.dart index 13c66ff01d..414b061fc0 100644 --- a/lib/pages/send_view/sol_token_send_view.dart +++ b/lib/pages/send_view/sol_token_send_view.dart @@ -46,6 +46,7 @@ import '../../wallets/models/tx_data.dart'; import '../../widgets/animated_text.dart'; import '../../widgets/background.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; +import '../../widgets/hideable_balance.dart'; import '../../widgets/icon_widgets/clipboard_icon.dart'; import '../../widgets/icon_widgets/qrcode_icon.dart'; import '../../widgets/icon_widgets/sol_token_icon.dart'; @@ -799,36 +800,42 @@ class _SolTokenSendViewState extends ConsumerState { }, child: Container( color: Colors.transparent, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.end, - children: [ - Text( - _formatTokenBalance( - ref - .watch( - pSolanaTokenBalance(( - walletId: - widget.walletId, - tokenMint: tokenMint, - )), - ) - .spendable, - ), - style: STextStyles.titleBold12( - context, - ).copyWith(fontSize: 10), - textAlign: TextAlign.right, - ), - if (price != null) + child: HideableBalance( + iconColor: Theme.of(context) + .extension()! + .textDark, + iconSize: 16, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ Text( - "${(ref.watch(pSolanaTokenBalance((walletId: widget.walletId, tokenMint: tokenMint))).spendable.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: locale)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", - style: STextStyles.subtitle( + _formatTokenBalance( + ref + .watch( + pSolanaTokenBalance(( + walletId: + widget.walletId, + tokenMint: tokenMint, + )), + ) + .spendable, + ), + style: STextStyles.titleBold12( context, - ).copyWith(fontSize: 8), + ).copyWith(fontSize: 10), textAlign: TextAlign.right, ), - ], + if (price != null) + Text( + "${(ref.watch(pSolanaTokenBalance((walletId: widget.walletId, tokenMint: tokenMint))).spendable.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: locale)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", + style: STextStyles.subtitle( + context, + ).copyWith(fontSize: 8), + textAlign: TextAlign.right, + ), + ], + ), ), ), ), diff --git a/lib/pages/send_view/sub_widgets/dual_balance_selection_sheet.dart b/lib/pages/send_view/sub_widgets/dual_balance_selection_sheet.dart index e4505bcd0e..6e664115ea 100644 --- a/lib/pages/send_view/sub_widgets/dual_balance_selection_sheet.dart +++ b/lib/pages/send_view/sub_widgets/dual_balance_selection_sheet.dart @@ -18,6 +18,7 @@ import '../../../utilities/constants.dart'; import '../../../utilities/text_styles.dart'; import '../../../wallets/crypto_currency/coins/firo.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../../widgets/hideable_balance.dart'; class DualBalanceSelectionSheet extends ConsumerStatefulWidget { const DualBalanceSelectionSheet({super.key, required this.walletId}); @@ -148,24 +149,30 @@ class _FiroBalanceSelectionSheetState textAlign: TextAlign.left, ), const SizedBox(width: 2), - Text( - ref - .watch(pAmountFormatter(coin)) - .format( - ref - .watch( - coin is Firo - ? pWalletBalanceTertiary( - walletId, - ) - : pWalletBalanceSecondary( - walletId, - ), - ) - .spendable, - ), - style: STextStyles.itemSubtitle(context), - textAlign: TextAlign.left, + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch(pAmountFormatter(coin)) + .format( + ref + .watch( + coin is Firo + ? pWalletBalanceTertiary( + walletId, + ) + : pWalletBalanceSecondary( + walletId, + ), + ) + .spendable, + ), + style: STextStyles.itemSubtitle(context), + textAlign: TextAlign.left, + ), ), ], ), @@ -238,16 +245,22 @@ class _FiroBalanceSelectionSheetState textAlign: TextAlign.left, ), const SizedBox(width: 2), - Text( - ref - .watch(pAmountFormatter(coin)) - .format( - ref - .watch(pWalletBalance(walletId)) - .spendable, - ), - style: STextStyles.itemSubtitle(context), - textAlign: TextAlign.left, + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch(pAmountFormatter(coin)) + .format( + ref + .watch(pWalletBalance(walletId)) + .spendable, + ), + style: STextStyles.itemSubtitle(context), + textAlign: TextAlign.left, + ), ), ], ), diff --git a/lib/pages/send_view/token_send_view.dart b/lib/pages/send_view/token_send_view.dart index 5601d41c9a..6af3ddcfb3 100644 --- a/lib/pages/send_view/token_send_view.dart +++ b/lib/pages/send_view/token_send_view.dart @@ -48,6 +48,7 @@ import '../../widgets/animated_text.dart'; import '../../widgets/background.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/eth_fee_form.dart'; +import '../../widgets/hideable_balance.dart'; import '../../widgets/icon_widgets/addressbook_icon.dart'; import '../../widgets/icon_widgets/clipboard_icon.dart'; import '../../widgets/icon_widgets/eth_token_icon.dart'; @@ -767,42 +768,50 @@ class _TokenSendViewState extends ConsumerState { }, child: Container( color: Colors.transparent, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.end, - children: [ - Text( - ref - .watch(pAmountFormatter(coin)) - .format( - ref - .watch( - pTokenBalance(( - walletId: - widget.walletId, - contractAddress: - tokenContract - .address, - )), - ) - .spendable, - tokenContract: - tokenContract, - ), - style: STextStyles.titleBold12( - context, - ).copyWith(fontSize: 10), - textAlign: TextAlign.right, - ), - if (price != null) + child: HideableBalance( + iconColor: Theme.of(context) + .extension()! + .textDark, + iconSize: 16, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ Text( - "${(ref.watch(pTokenBalance((walletId: widget.walletId, contractAddress: tokenContract.address))).spendable.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: locale)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", - style: STextStyles.subtitle( + ref + .watch( + pAmountFormatter(coin), + ) + .format( + ref + .watch( + pTokenBalance(( + walletId: widget + .walletId, + contractAddress: + tokenContract + .address, + )), + ) + .spendable, + tokenContract: + tokenContract, + ), + style: STextStyles.titleBold12( context, - ).copyWith(fontSize: 8), + ).copyWith(fontSize: 10), textAlign: TextAlign.right, ), - ], + if (price != null) + Text( + "${(ref.watch(pTokenBalance((walletId: widget.walletId, contractAddress: tokenContract.address))).spendable.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: locale)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", + style: STextStyles.subtitle( + context, + ).copyWith(fontSize: 8), + textAlign: TextAlign.right, + ), + ], + ), ), ), ), diff --git a/lib/pages/shopinbit/shopinbit_send_from_view.dart b/lib/pages/shopinbit/shopinbit_send_from_view.dart index f43e22f210..ef7939bf12 100644 --- a/lib/pages/shopinbit/shopinbit_send_from_view.dart +++ b/lib/pages/shopinbit/shopinbit_send_from_view.dart @@ -34,6 +34,7 @@ import '../../widgets/conditional_parent.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/desktop/desktop_dialog.dart'; import '../../widgets/desktop/desktop_dialog_close_button.dart'; +import '../../widgets/hideable_balance.dart'; import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_dialog.dart'; import '../home_view/home_view.dart'; @@ -500,20 +501,32 @@ class _ShopInBitSendFromCardState extends ConsumerState { contractAddress: tokenContract!.address, )), ); - return Text( - "${balance.spendable.decimal.toStringAsFixed(tokenContract!.decimals)} ${tokenContract!.symbol}", - style: STextStyles.itemSubtitle(context), + return HideableBalance( + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + "${balance.spendable.decimal.toStringAsFixed(tokenContract!.decimals)} ${tokenContract!.symbol}", + style: STextStyles.itemSubtitle(context), + ), ); }, ) else - Text( - ref - .watch(pAmountFormatter(coin)) - .format( - ref.watch(pWalletBalance(walletId)).spendable, - ), - style: STextStyles.itemSubtitle(context), + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch(pAmountFormatter(coin)) + .format( + ref.watch(pWalletBalance(walletId)).spendable, + ), + style: STextStyles.itemSubtitle(context), + ), ), ], ), diff --git a/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_address_from_stack.dart b/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_address_from_stack.dart index 6dd9b2fe74..2a7062f58b 100644 --- a/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_address_from_stack.dart +++ b/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_address_from_stack.dart @@ -28,6 +28,7 @@ import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart'; import '../../../widgets/custom_buttons/blue_text_button.dart'; import '../../../widgets/desktop/secondary_button.dart'; +import '../../../widgets/hideable_balance.dart'; import '../../../widgets/icon_widgets/x_icon.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_dialog.dart'; @@ -234,12 +235,16 @@ class _BalanceDisplay extends ConsumerWidget { ref.watch(pWalletBalanceSecondary(walletId)).total + ref.watch(pWalletBalanceTertiary(walletId)).total; - return Text( - ref.watch(pAmountFormatter(coin)).format(total), - style: STextStyles.desktopTextExtraSmall(context).copyWith( - color: Theme.of(context).extension()!.textSubtitle1, + return HideableBalance( + iconColor: Theme.of(context).extension()!.textSubtitle1, + iconSize: 16, + child: Text( + ref.watch(pAmountFormatter(coin)).format(total), + style: STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of(context).extension()!.textSubtitle1, + ), + textAlign: TextAlign.right, ), - textAlign: TextAlign.right, ); } } diff --git a/lib/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart b/lib/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart index 866e6a6ca7..3019172d3b 100644 --- a/lib/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart @@ -29,6 +29,7 @@ import '../../../utilities/text_styles.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../widgets/desktop/desktop_dialog.dart'; import '../../../widgets/desktop/desktop_dialog_close_button.dart'; +import '../../../widgets/hideable_balance.dart'; import '../../../widgets/rounded_white_container.dart'; import '../wallet_view/sub_widgets/desktop_send.dart'; @@ -127,32 +128,38 @@ class _DesktopPaynymSendDialogState const Spacer(), Container( color: Colors.transparent, - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - ref - .watch(pAmountFormatter(coin)) - .format( - ref - .watch(pWalletBalance(widget.walletId)) - .spendable, - ), - style: STextStyles.titleBold12(context), - textAlign: TextAlign.right, - ), - if (price != null) const SizedBox(height: 2), - if (price != null) + child: HideableBalance( + iconColor: Theme.of(context) + .extension()! + .textDark, + iconSize: 16, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ Text( - "${(ref.watch(pWalletBalance(widget.walletId)).spendable.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: locale)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", - style: STextStyles.baseXS(context).copyWith( - color: Theme.of(context) - .extension()! - .textSubtitle1, - ), + ref + .watch(pAmountFormatter(coin)) + .format( + ref + .watch(pWalletBalance(widget.walletId)) + .spendable, + ), + style: STextStyles.titleBold12(context), textAlign: TextAlign.right, ), - ], + if (price != null) const SizedBox(height: 2), + if (price != null) + Text( + "${(ref.watch(pWalletBalance(widget.walletId)).spendable.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: locale)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", + style: STextStyles.baseXS(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ), + textAlign: TextAlign.right, + ), + ], + ), ), ), ], diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart index b411775efe..274b192597 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart @@ -73,6 +73,7 @@ import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/dialogs/firo_exchange_address_dialog.dart'; import '../../../../widgets/epic_txs_method_toggle.dart'; import '../../../../widgets/eth_fee_form.dart'; +import '../../../../widgets/hideable_balance.dart'; import '../../../../widgets/icon_widgets/addressbook_icon.dart'; import '../../../../widgets/icon_widgets/clipboard_icon.dart'; import '../../../../widgets/icon_widgets/qrcode_icon.dart'; @@ -1531,19 +1532,25 @@ class _DesktopSendState extends ConsumerState { style: STextStyles.itemSubtitle12(context), ), const SizedBox(width: 10), - Text( - ref - .watch(pAmountFormatter(coin)) - .format( - ref - .watch( - isMwebEnabled - ? pWalletBalanceSecondary(walletId) - : pWalletBalanceTertiary(walletId), - ) - .spendable, - ), - style: STextStyles.itemSubtitle(context), + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch(pAmountFormatter(coin)) + .format( + ref + .watch( + isMwebEnabled + ? pWalletBalanceSecondary(walletId) + : pWalletBalanceTertiary(walletId), + ) + .spendable, + ), + style: STextStyles.itemSubtitle(context), + ), ), ], ), @@ -1557,13 +1564,19 @@ class _DesktopSendState extends ConsumerState { style: STextStyles.itemSubtitle12(context), ), const SizedBox(width: 10), - Text( - ref - .watch(pAmountFormatter(coin)) - .format( - ref.watch(pWalletBalance(walletId)).spendable, - ), - style: STextStyles.itemSubtitle(context), + HideableBalance( + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch(pAmountFormatter(coin)) + .format( + ref.watch(pWalletBalance(walletId)).spendable, + ), + style: STextStyles.itemSubtitle(context), + ), ), ], ), diff --git a/test/wallets/support/xelis_test_fakes.dart b/test/wallets/support/xelis_test_fakes.dart index 06ff17da81..1c3ecac5b6 100644 --- a/test/wallets/support/xelis_test_fakes.dart +++ b/test/wallets/support/xelis_test_fakes.dart @@ -101,6 +101,8 @@ class ConfirmationPrefs extends ChangeNotifier implements Prefs { @override String get currency => 'USD'; @override + bool get hideBalances => false; + @override dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } diff --git a/test/widget_tests/hide_balances_coverage_test.dart b/test/widget_tests/hide_balances_coverage_test.dart index 3652fb9736..55d935b210 100644 --- a/test/widget_tests/hide_balances_coverage_test.dart +++ b/test/widget_tests/hide_balances_coverage_test.dart @@ -6,6 +6,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'; import 'package:stackwallet/models/isar/stack_theme.dart'; +import 'package:stackwallet/pages/send_view/sub_widgets/dual_balance_selection_sheet.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart'; import 'package:stackwallet/providers/global/prefs_provider.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart'; @@ -74,6 +75,7 @@ void main() { pWalletName(walletId).overrideWithValue("some wallet"), pWalletCoin(walletId).overrideWithValue(coin), pWalletBalance(walletId).overrideWithValue(balance), + pWalletBalanceSecondary(walletId).overrideWithValue(balance), pWalletIsFavourite(walletId).overrideWithValue(false), ], child: MaterialApp( @@ -129,4 +131,14 @@ void main() { find.text(formatted), ); }); + + testWidgets("the send balance selection sheet masks its balances", ( + tester, + ) async { + await expectMasking( + tester, + () => const DualBalanceSelectionSheet(walletId: walletId), + find.text(formatted), + ); + }); } From 7ace65344b5675742ab25733860ab3cb858507f4 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 6 Oct 2026 21:51:41 -0500 Subject: [PATCH 6/6] feat: mask balances in the Solana token list and sync options --- .../wallet_syncing_options_view.dart | 42 ++++++++++++------- .../sub_widgets/sol_token_select_item.dart | 35 +++++++++++----- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/lib/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart b/lib/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart index 9e8d66a1de..619653ee10 100644 --- a/lib/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart +++ b/lib/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart @@ -27,6 +27,7 @@ import '../../../../widgets/background.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; +import '../../../../widgets/hideable_balance.dart'; import '../../../../widgets/rounded_white_container.dart'; class WalletSyncingOptionsView extends ConsumerWidget { @@ -150,22 +151,31 @@ class WalletSyncingOptionsView extends ConsumerWidget { ), ), const SizedBox(height: 2), - Text( - ref - .watch( - pAmountFormatter(info.coin), - ) - .format( - ref - .watch( - pWalletBalance( - info.walletId, - ), - ) - .total, - ), - style: STextStyles.itemSubtitle( - context, + HideableBalance( + key: ValueKey( + "hideBalance_${info.walletId}", + ), + iconColor: Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + ref + .watch( + pAmountFormatter(info.coin), + ) + .format( + ref + .watch( + pWalletBalance( + info.walletId, + ), + ) + .total, + ), + style: STextStyles.itemSubtitle( + context, + ), ), ), ], diff --git a/lib/pages/token_view/sub_widgets/sol_token_select_item.dart b/lib/pages/token_view/sub_widgets/sol_token_select_item.dart index 03d048947d..47d8845b6f 100644 --- a/lib/pages/token_view/sub_widgets/sol_token_select_item.dart +++ b/lib/pages/token_view/sub_widgets/sol_token_select_item.dart @@ -27,6 +27,7 @@ import '../../../wallets/wallet/impl/sub_wallets/solana_token_wallet.dart'; import '../../../wallets/wallet/wallet.dart'; import '../../../widgets/desktop/primary_button.dart'; import '../../../widgets/dialogs/basic_dialog.dart'; +import '../../../widgets/hideable_balance.dart'; import '../../../widgets/icon_widgets/sol_token_icon.dart'; import '../../../widgets/rounded_white_container.dart'; import '../sol_token_view.dart'; @@ -195,16 +196,30 @@ class _SolTokenSelectItemState extends ConsumerState { : STextStyles.titleBold12(context), ), const Spacer(), - Text( - balanceString, - style: isDesktop - ? STextStyles.desktopTextExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ) - : STextStyles.itemSubtitle(context), + HideableBalance( + key: ValueKey( + "hideBalance_${widget.walletId}_" + "${widget.token.address}", + ), + iconColor: isDesktop + ? Theme.of(context) + .extension()! + .textDark + : Theme.of(context) + .extension()! + .infoItemLabel, + iconSize: 16, + child: Text( + balanceString, + style: isDesktop + ? STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ) + : STextStyles.itemSubtitle(context), + ), ), ], ),